From 9ebca738ab560c213c3c15f01b1841759615df04 Mon Sep 17 00:00:00 2001 From: Marcin Abramowicz <44381959+abrams27@users.noreply.github.com> Date: Tue, 15 Feb 2022 16:23:22 +0100 Subject: [PATCH] [pre-release] no more `dev` branch (#148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change project structure (#12) * [1 - Initial code refactor] basic classes extraction (#13) * proces lock is a semaphore * process start methods moved to the new class * Build.Query moved to class * targets options moved to the class * action graph parser moved to the class * getMnemonics moved to the class * packages rearrangement * scala sbt server moved to the class * java sbt server moved to the class * buildClientLogger + methods bodies moved uder calls * brackets in ifs * rebase * proper enterprise package structure * BUILD files in subpackages * packages without java prefix * logger todo * imports are sorted * Configure CI. (#16) * Configure CI. * Change job name. * Add ls. * Update java.ci.yml * Update java.ci.yml * Update java.ci.yml * Fix test.sh * Comment out tests. * Update java.ci.yml * Update test.sh * Revert comment * Uncomment one test * Uncomment more tests * Comment test * Uniform commenting * Uncomment one test * Uncomment one test * Uncomment one test * Uncomment one test * Uncomment one test * Uncomment one test * Uncomment one test * Update BazelBspServerTest.java * Add master * Uncomment tests * Increase time limit of tests * Increase time limit of tests * Remove master * Increase time limit of tests * Increase time limit of one test * Decrease total time * Add master * Refactor pt 2 (#18) - Introduced `BazelRunner` class which is fully responsible for starting processes, awaiting them and gathering results - Moved synchronized `get` methods to `BazelDataResolver`. They are no longer required to be synchronized, as all the required data is fetched in the constructor of `BazelBspServer` - Removed checked exceptions in many places and the need for try-to-rethrow - Removed `Either`s in many places where they were no longer needed - Corrected several minor issues (`setBackendPort` appending to a constant, `convertExitCode` not using enum names) * Add a CI job for formatting (#20) Add a CI job for formatting * Test refactor (#19) * data moved to the separete class * runner extraction + additional methods extraction * workspace field is now in data class + constructor argument removal * timeouts moved to the data class * methods refactor * .test.sh update * logger * test logs * one more log * minor reformat * lombok dep removal * code reformat * buildifier * fix * [configuration] Optional argument on server startup (#15) * [configuration] Optional argument on server startup Add a optional parameters when starting a server to specify the target that should be built. * BEP Server refactor (#21) * Separating interface implementations and general dependency structure refactor (#24) * Extracted ScalaBuildServer implementation * Extracted JavaBuildServer implementation, both classes now implement proper interfaces * Extracted BuildServer endpoints, very tentatively (previous problems with it) * Extracted BuildServer implementation more properly * Tidied up initialized/finished methods + other smaller * Extracted several things to ParsingUtils and Constants * Extracted BazelBspServerLifetime and BazelBspRequestHelpers, Java- and ScalaBuildServer classes now not dependent on BazelBspServer * Added server config, Bazel runner and Bazel data dependencies to the server implementation * Adjusted BazelData dependencies * Extracted ServerBuildManager * Hotfix of the cyclical dependency, to improve iteratively * Moved part of the BSP integration in Server to the BazelBspServer constructor * Added further parts to the constructor integration * Removed cyclical dependency between BuildManager and BuildServerImpl * Started removing cyclical dependency between BazelBspServer and BepServer: BazelData passed in the constructor * Resolved cyclical dependency between BuildManager and BepServer * Cleaned up in BazelBspServer * Simplified the constructor, moved away logic * General cleanup * Extracted suppliers from interface methods in BuildServerImpl * Added build server implementations as fields * Prepared for interface and service separation * Separated BuildServer logic from interface implementation * Separated logic from interface implementation for Scala and Java BuildServers * Extracted BEP package * Created new packages and grouped the files + small style improvements * Minor style corrections * Renamed Server.java, imported io.grpc.Server * Corrections after review * Naming and packaging changes * Formatting fix * Formatting fix * bazel runner refactor (#23) * package update * BazelRunner logs + minor change * ExitCodeMapper * runBazelCommandBes update * BazelRunnerUpdate v2 * BazelTestRunner * BazelCleanRunner * BazelRunRunner * BazelBuildRunner * BazelInfoRunner * BazelActionGraphQueryRunner * BazelQueryRunner * renames * bazel runner builder * packages update * QueryResolver update * code reformat * buldifier * targets fix * format fix * format fix * format fix v2 * format fix v3 * format fix v4 * rebase * review fix * reformat * test fix * query kind param * reformat * review fix * reformat * packages rename * runner lists creation update * refactor * common -> commons * resolver -> resolvers * service -> services * util -> utils * Packages rearrange + Utils refactor (#30) * local services setter update * .../impl -> .../bsp/impl * .../services -> .../bsp/services * ClasspathParser * StderrDiagnosticsParser * cleanup * BazelBspServerTestData rollback * .../resolvers -> .../bsp/resolvers * reformat * ActionGraphParser moved to resolvers * ParsingUtils update * reformat * flag fix * review fix * setLocalService rollback * reviw fix * getRuleType rollback * rename * [Post BazelBuilder refactor part 1] TargetsResolver update (#33) * BuildClientLogger does NOT throw an exception * BuildClientLogger moved to bep package * TargetsUtils now maps targets to uris * .../resolvers/targets package * Scala/JavaBuildServerService constructor update * TargetsResolver constructor update * TargetsResolver builder * TargetsResolver refactor * targets package removal * reformat * review fixes * unused import + targetsResolver constructor visibility * [Post BazelBuilder refactor part 2] TargetsRuleResolver (#34) * ActionGraphParser getInputs more optimal implementation * typo in tests * TargetsResolver -> TargetsLanguageOptionsResolver * TargetsRulesResolver * reformat * TargetsRulesResolver -> TargetRulesResolver * Solving problems with Scala- and Java-specific endpoints (#32) * Added an aggregate build server implementation file for passing to the launcher * Renamed the hub class * Moved the hub class to server/bsp * buildTargetScalaTestClasses implementation (#35) * method signature update * poc * logic implementation * getSrcsPaths signature rollback * reformat * Apply suggestions from code review Co-authored-by: Magdalena Augustyńska <32980499+magda-aug@users.noreply.github.com> * rules filter update * main_class is the only source + constants update * targets resolver is created only once in constructor * reformat * hasGivenName * test * test fix * reformat * success typo Co-authored-by: Magdalena Augustyńska <32980499+magda-aug@users.noreply.github.com> * Implement buildTargetScalaMainClasses (#36) * Init * Fix resolving target options * Fix variable name * Change access * Fix formatting * Fix formatting * Read arguments * Upgrade bsp-testkit * Add empty test * Fix formatting * Move constants * Fix * Add more data to test * Add package name * Move targetRulesResolver to constructor * return Completable Future instead of null * Extract method * Extract isAttributeSpecifiedAndHasGivenName * Call buildServerImpl * Pass compiler options and args to test * Fix tests * Fix tests * Add more args to test * Read also jvm flags * Move to constants * Read only jvm flags * fix formatting * unify naming * use helper method * remove duplicate methods * Add more test cases * revert * Add test case * target without args test case * increase timeout * add test case without jvm flags * increase timeout * increase timeout * buildClient is now used in the bazel runner (#41) * buildClient is now used in bazel runner * format * bazel runner doesnt throw an exception anymore * javacoptions and scalacoptions tests (#37) * scalacopts test * javacopts test * reformat * buildifier * java_binary import * test fix * testkit vestion bump + test fix * more descriptive targets names * scalac classDirectory and classpath test update * .bazelversion file * __main__/bazel-out/darwin-fastbuild/ removal * workspaceReload implementation (#38) * bazel builder sync * workspaceReload PoC * bazel sync -> bazel fetch * reformat * dev branch import fix (#46) * executeBazelCommand -> executeBazelBesCommand * bazel call * bazel args checker removal * bazel info update * log update * log config * log level update * config fix * log fix * fix * log * more logging * XDXD * LOL2 * bazel runner update * reformat * initializer clean up * reformat * exit code to constant * date in logger * bazelProcess moved to the bazel package * refomat * Prohibit wildcard imports using error-prone (#45) So apparently error-prone is built into Bazel and provides a check for wildcard imports. http://errorprone.info/docs/installation * script for building and running server (#47) * filtering blank strings from build progress logs (#50) * filtering blank strings * trim rollback * isBlank -> isEmpty * additional trim * Fix build calls and outgroup groups interpretation (#49) * Add builder without mnemonics for build command * Change valid parts of classpath parsing * Switch bazel build calls to use withArguments * Action graph parser v2 (#42) * scalacopts test * javacopts test * reformat * buildifier * scalac classDirectory and classpath test update * Parse action graph v2 for bazel versions over 4.0.0 * Fix test * Add tests for different bazel versions * Comparison of semantic versions * Format * Get semantic version out of `bazel info release` command * Extract action graph v1 tests * Action graph parser method reorder * Explicitly get semantic version parts * Remove redundant return * Refactor action graph parsers * Remove logs for creation of test clients * Refactor semantic version comparator * Remove comment from .bazelignore * Cleanup semantic version * Streamify depset expansion to artifacts * Remove default visibility from new concat method for strings * Fix depset expansion when common nodes are traversed * semantic version comparator is now static * Improve readability of action graph parser * Change test logging * Remove wildcard imports Co-authored-by: Abrams27 * >=info logs are separate now (#51) * separate logs * new logging levels * appending logs * Get jdk info (#43) * scalacopts test * javacopts test * reformat * buildifier * scalac classDirectory and classpath test update * Parse action graph v2 for bazel versions over 4.0.0 * Fix test * Add tests for different bazel versions * Comparison of semantic versions * Format * Get semantic version out of `bazel info release` command * Extract action graph v1 tests * Action graph parser method reorder * Explicitly get semantic version parts * Remove redundant return * Refactor action graph parsers * Remove logs for creation of test clients * Refactor semantic version comparator * Remove comment from .bazelignore * Cleanup semantic version * Streamify depset expansion to artifacts * Remove default visibility from new concat method for strings * Fix depset expansion when common nodes are traversed * semantic version comparator is now static * Improve readability of action graph parser * Change test logging * Remove wildcard imports * Get java home from default settings * Get java version from aspect * Get java version through aspect * Refactor jvm build target data fetching * Refactor jvm build target logic * Map java home to uri * Add java projects tests * Remove ignored files * Format * Remove action graph logs * Add new project directories to tests script * Update BSP dependency Co-authored-by: Abrams27 * Refactor build manager into several build managers commit 3fc3f660459cdfeba82a5a66bfbcf3dfbf4f497d Author: Andre Rocha Date: Thu Apr 15 15:39:50 2021 +0000 Add no build flag to aspects commit 3364cd10374dccd6cb59fd20515c886078abe3c7 Author: Andre Rocha Date: Thu Apr 15 12:29:16 2021 +0000 Add manager for aspects commit 935c5fc29c1f4a12fee61cc6d297715657ccaa7b Author: Andre Rocha Date: Wed Apr 14 12:00:30 2021 +0000 Update refactor according to new changes commit 936ac94b1e0c360aa19846dce6a54b7d7afb29d0 Author: Andre Rocha Date: Mon Mar 22 18:05:17 2021 +0000 Refactor BazelBspServerBuildManager into several different services commit 4dd0b01fd52af9ebe7bb55ff7a974b06816f356e Author: Andre Rocha Date: Fri Feb 26 15:46:40 2021 +0000 Get semantic version out of `bazel info release` command commit c594dc129454bfc707738fd7438f0e789f7859bd Author: Andre Rocha Date: Tue Feb 16 16:43:52 2021 +0000 Fix test commit f931ccda89585ed248dfca7518a0ed02cc5a0dd5 Author: Andre Rocha Date: Mon Feb 15 17:17:08 2021 +0000 Parse action graph v2 for bazel versions over 4.0.0 commit 5f8f55f33ba4a80ff7f3f2cc4e3656b816b01a54 Author: Abrams27 Date: Sat Feb 6 21:53:54 2021 +0100 scalac classDirectory and classpath test update commit a2088c51e22e57e40ce255fdebec5eae9feebea5 Author: Abrams27 Date: Sat Jan 30 23:21:16 2021 +0100 buildifier commit 9dbb63c792c3a2ba7fa633b09b33d6e4604177a8 Author: Abrams27 Date: Sat Jan 30 23:20:21 2021 +0100 reformat commit 57f6fa69e0ad8ad03b7a942cb7a724e1b5d90d45 Author: Abrams27 Date: Sat Jan 30 23:13:22 2021 +0100 javacopts test commit 26abd73b34d06ff4db1fa620c3dbfd320eb28bc1 Author: Abrams27 Date: Sat Jan 30 22:38:32 2021 +0100 scalacopts test commit c0591d00f972b598781fa27213845f31b8d3ebe5 Author: Andre Rocha Date: Tue Mar 23 19:47:12 2021 +0000 Add builder without mnemonics for build command * Add lazy variables Squashed commits of the following: commit 3c83a38d14985b6ab309a945723d5100ef6dad72 Author: Andre Rocha Date: Fri Apr 16 12:10:26 2021 +0000 Lazy vals recalculated on workspace reload commit 5ff6d625405ddb713345998b5394e045e80a0ea8 Author: Andre Rocha Date: Thu Apr 15 23:15:28 2021 +0000 Add lazy vals for complicated calculations * add Apache 2 license and badges * Entire repository import test (#53) * testkit version bump * test * todo * style * style v2 * ci fix 1 * ci fix * variable update + gitignore update * Add cpp support to packages: commit c8c38f865c0b151a9fcdbdff3992a16a7a9bb8bd Author: Andre Rocha Date: Fri May 14 10:14:44 2021 +0000 Add cpp folder to tests commit 686b6576696eb1ad9e6279d3067deb7bea086b09 Merge: b0b06a3 5032915 Author: Andre Rocha Date: Thu May 13 22:44:35 2021 +0000 Merge branch 'dev' into cpp-extension commit b0b06a342bcf372ab2487a1fe1b8bb147c645182 Author: Andre Rocha Date: Thu May 13 15:30:35 2021 +0000 Add cpp dependency commit b7cd44d12b7f0bdcee54a47119fba2d14b8ccbac Author: Andre Rocha Date: Wed Apr 14 11:12:29 2021 +0000 Add more options to cpp target example commit 483d9fdf4ce1b3889f352968c283f6c9e0091645 Author: Andre Rocha Date: Tue Apr 13 22:14:00 2021 +0000 Tests for cpp commit 73bc827170f3b7d0bf78a7c602f2e04fcd9be51e Author: Andre Rocha Date: Thu Apr 8 18:11:36 2021 +0000 Updated cpp options command call commit dfa21f41d08fe2a4b605ad44e80901fb5c0ee349 Author: Andre Rocha Date: Wed Mar 17 19:01:18 2021 +0000 Add cpp extension method service commit 9fa25dde72d24437776f8f10f8ae33b8cf79a8c3 Author: Andre Rocha Date: Tue Mar 16 18:48:17 2021 +0000 Add cpp build target * Remove java 11 methods * Squashed commit of the following: commit a9bac730d1452df0a38ceba046cc5bd82faf647e Author: Andre Rocha Date: Tue May 25 22:32:53 2021 +0000 Add bazel-sonatype dependency * Update scala repository checksums * ProjectView files parser implementation (#55) * ProjectView files parser implementation * code reformat * buildifier * ci tests * character variable extraction * reading test config from file * java docs * parse default method * google format * google format v2 * PoC: First, very very very basic projectview usage (#57) * reading file * projecview provider * projectview provider usage * logic * deps * fix * buildifier * google format * config removal * google format v2 * test fix * google format v3 * kind moved to utils * config rollback * fix * Guarantee deps are built before finishing import. (#56) * Guarantee deps are built before finishing import. Dependencies must be fully build in order to guarantee the dependencies are all present for indexing. * Increase tests timeout * Refactor BazelBspCompilationManager for more readability * Refactor targets union to TargetsUtils * [Projectview] import support (#58) * DirectoriesSectionParserTest tests rename * TargetsSectionParserTest tests rename * ProjectViewParserImplTest tests rename * ProjectViewSectionSplitterTest tests rename * import test * splitter test update * ProjectImportParser * merge * parser + test adjustment * buildifier * format * format v2 * format v3 * format v4 * format v5 * format v6 * format v7 * format v8 * Update README.md * Update README.md * [fix] projectview - tab symbol parsing (#66) * tabs fix + test * tests update * google style * google style v2 * [fix] Semantic version - more generic support (#68) * tests * homebrew version test * impl fix * google style * [fix] 'Loading: 0 packages loaded' message prohibition (#71) [fix] 'Loading: 0 packages loaded' message prohibition * [fix] java 8 feature fix (#73) * installation process simplification (#76) * depdendencies update * install script update * instalation location * Update README.md * Update README.md * Update README.md * version bump * RC1 version * [fix] bazel WORKSPACE warnings fix (#81) * sha update * workspace update * 404 fix? * main/src ... removal (#82) * [change] Integration tests upgrade (#83) * test resources -> e2e * .bazelignore update * sample repo new test * style + buildifier * sample repo bash runner * BazelBspActionGraphV1Test test * style * BazelBspActionGraphV2Test + build files update * BazelBspJava11ProjectTest * BazelBspJava8ProjectTest * BazelBspEntireRepositoryImportTest + style * BazelBspCppProjectTest * e2e:all * buildifier * [change] e2e tests CI update (#84) * ci update * test scrip fix * shared cache * old tests removal (#86) * [change] multimode project (#87) * projectview extraction * commons extraction * e2e visibility update * bazelrunner extraction * install extraction * server extraction * readme update * [change] unit tests ci update (#89) * [change] docs update (#91) * README - tests section * Create BUMPVERSION.md * [fix] Semantic Versioning regex update (#93) * rolling release test * regex update * [added] changelog (#97) * 1.0.0 changelog * Update CHANGELOG.md * [fixed] targets from `exports` attribute are passed to the BSP deps attribute (#98) * daemon typo fix * test update + fix * BuildRuleAttributeExtractor * BazelBspQueryManager update * ScalaBuildServerService update * build fix * TargetsLanguageOptionsResolver update * google format * ci update * google format * google style * .getExplicitlySpecified * google style * Update CHANGELOG.md * Update CHANGELOG.md * [fixed] scala test rules include `scala_specs2_junit_test` (#101) * e2e test * buildifier * scala_specs2_junit_test should be included in model now * buildifier * test update * Update CHANGELOG.md * Update CHANGELOG.md * format * fix * [release-preparation] 1.0.1 (#102) * version bump * changelog * [release-preparation] cleanup * [release-preparation] final cleanup * Allow running a Bazel command without [positional] arguments One command that doesn't accept arguments at all (only flags) is `bazel clean`. * [change] better heuristics for guessing source roots (#126) * Better heuristics for guessing source roots * refactoring * formatting * no recursion * refactoring and test * Refactoring and changelog * more refactoring * Add JvmBuildServer implementation to fix running tests * [change] bump java to 11 (#129) Co-authored-by: Lukasz Wawrzyk * [feature] handle multiple projects in one workspace (#130) Co-authored-by: Lukasz Wawrzyk * [fix] project bazel version switched to 3.7.2 (#141) * [fix] .bazelversion file * [fix] changelog upadate * [fix] badges in readme (#142) * Update README.md * Integration tests badge * format badge * unit tests badge * return only the relevant output jar rather than 5-6 jars including sources, headers etc. (#136) * build project during sync (#137) * Filter out non runtime jars for running apps and tests (#131) * Filter out non runtime jars * use aspect to get runtime classpath * improve test framework * [feature] project view 2000 (#143) * project view parser major refactor * bazel_path section * i love java 11 * style * buildifier * style v2 * debugger_address section * i love java 11 more * debugger_address section part 2 * splitter tests update * splitter test fix * java_path section * comments * project view usages * scala.md fix * e2e test resources fix * e2e test resources fix part 2 * [e2e] cleaning environment before installation * tests update * new mechanism usage * fix * visibility and toString() impl * little test change * style * another little test fix * targets visibility update * i really love tests * try > java exceptions * builidier * visibility update * more visibility update * format * java docs * compareByName -> hasName * instanceOf -> getInstance * split -> getRawSectionsForFileContent * anoter -> another_ * list -> stream * commons in ci * getInstance -> createInstance * readme update * format * Update CHANGELOG.md * Update CHANGELOG.md * targets validation out * bazel in install update * even more tests * [fix] error diagnostics with correct position (#146) * new FileDiagnostic * new StderrDiagnosticsParser * usages * Update CHANGELOG.md * [change] handle aspects when multiple projects are in workspace (#132) Co-authored-by: Andrzej Głuszak Co-authored-by: Magdalena Augustyńska <32980499+magda-aug@users.noreply.github.com> Co-authored-by: ex0ns Co-authored-by: Gerard Drozdz Co-authored-by: André Rocha Co-authored-by: Andre Rocha Co-authored-by: Justin Kaeser Co-authored-by: odisseus Co-authored-by: Lukasz Wawrzyk Co-authored-by: Łukasz Wawrzyk Co-authored-by: Łukasz Wawrzyk --- .bazelrc | 4 +- .bazelversion | 1 + .github/workflows/integration-tests.yml | 34 +- .github/workflows/unit-tests.yml | 33 +- CHANGELOG.md | 19 +- README.md | 8 +- .../bazel/bazelrunner/BazelDataResolver.java | 3 +- .../bsp/bazel/bazelrunner/BazelRunner.java | 27 +- .../bsp/bazel/bazelrunner/data/BazelData.java | 11 +- .../org/jetbrains/bsp/bazel/commons/BUILD | 1 + .../bsp/bazel/commons/BetterFiles.java | 17 + .../bsp/bazel/commons/Constants.java | 8 +- .../org/jetbrains/bsp/bazel/commons/BUILD | 14 + .../bsp/bazel/commons/BetterFilesTest.java | 45 ++ docs/BUMPVERSION.md | 2 + docs/kotlin.md | 8 +- docs/scala.md | 8 +- e2e/runTest.sh | 9 +- .../bsp/bazel/BazelBspSampleRepoTest.java | 64 +- .../bazel/base/BazelBspTestBaseScenario.java | 6 +- .../bazel/base/BazelBspTestScenarioStep.java | 2 +- install.sh | 4 + .../org/jetbrains/bsp/bazel/install/BUILD | 6 +- .../jetbrains/bsp/bazel/install/Install.java | 75 ++- .../jetbrains/bsp/bazel/install/aspects.bzl | 17 + .../install/default-projectview.bazelproject | 5 + pom.xml | 3 +- projectview/BUILD | 3 +- projectview/README.md | 247 ++++++++ .../bsp/bazel/projectview/model/BUILD | 7 +- .../bazel/projectview/model/ProjectView.java | 213 +++++-- .../model/ProjectViewDefaultProvider.java | 19 - .../model/ProjectViewProvider.java | 4 +- .../bazel/projectview/model/sections/BUILD | 9 +- .../sections/ProjectViewBazelPathSection.java | 15 + .../ProjectViewDebuggerAddressSection.java | 15 + .../sections/ProjectViewJavaPathSection.java | 15 + .../sections/ProjectViewListSection.java | 47 ++ .../model/sections/ProjectViewSection.java | 12 +- .../sections/ProjectViewSectionHeader.java | 16 - .../sections/ProjectViewSingletonSection.java | 30 + .../sections/ProjectViewTargetsSection.java | 26 + .../projectview/model/sections/specific/BUILD | 11 - .../sections/specific/DirectoriesSection.java | 66 --- .../sections/specific/TargetsSection.java | 65 -- .../bsp/bazel/projectview/parser/BUILD | 8 +- .../ProjectViewDefaultParserProvider.java | 22 +- .../parser/ProjectViewImportParser.java | 49 -- .../projectview/parser/ProjectViewParser.java | 104 +++- .../parser/ProjectViewParserImpl.java | 96 +-- .../parser/ProjectViewRawSectionParser.java | 39 -- .../bazel/projectview/parser/sections/BUILD | 15 +- .../ProjectViewBazelPathSectionParser.java | 16 + ...ojectViewDebuggerAddressSectionParser.java | 16 + .../ProjectViewJavaPathSectionParser.java | 16 + .../ProjectViewListSectionParser.java | 86 ++- .../sections/ProjectViewSectionParser.java | 54 +- .../ProjectViewSingletonSectionParser.java | 40 ++ .../ProjectViewTargetsSectionParser.java | 18 + .../parser/sections/specific/BUILD | 12 - .../specific/DirectoriesSectionParser.java | 31 - .../specific/TargetsSectionParser.java | 21 - .../bazel/projectview/parser/splitter/BUILD | 6 +- .../splitter/ProjectViewRawSection.java | 31 +- .../splitter/ProjectViewRawSections.java | 42 ++ .../ProjectViewSectionHeaderPosition.java | 27 - .../splitter/ProjectViewSectionSplitter.java | 121 ++-- .../bsp/bazel/projectview/model/BUILD | 13 + .../projectview/model/ProjectViewTest.java | 512 ++++++++++++++++ .../bazel/projectview/model/sections/BUILD | 25 + .../sections/ProjectViewListSectionTest.java | 81 +++ .../ProjectViewSingletonSectionTest.java | 75 +++ .../bsp/bazel/projectview/parser/BUILD | 22 +- .../parser/ProjectViewParserImplTest.java | 560 ++++++++++++++++-- .../parser/ProjectViewParserMockTestImpl.java | 37 +- .../parser/sections/{specific => }/BUILD | 12 +- ...ProjectViewSingletonSectionParserTest.java | 221 +++++++ .../ProjectViewTargetsSectionParserTest.java | 217 +++++++ .../DirectoriesSectionParserTest.java | 83 --- .../specific/TargetsSectionParserTest.java | 83 --- .../bazel/projectview/parser/splitter/BUILD | 25 + .../splitter/ProjectViewRawSectionTest.java | 55 ++ .../splitter/ProjectViewRawSectionsTest.java | 138 +++++ .../ProjectViewSectionSplitterTest.java | 275 +++++++-- .../test/resources/{projectview => }/BUILD | 2 +- .../resources/projectview/empty.bazelproject | 5 + .../resources/projectview/file1.bazelproject | 20 + .../resources/projectview/file2.bazelproject | 3 + .../resources/projectview/file3.bazelproject | 9 + .../file4ImportsFile1.bazelproject | 6 + .../file5ImportsFile1File2File3.bazelproject | 7 + .../file6ImportsFile2File3File4.bazelproject | 3 + .../file7ImportsFile1.bazelproject | 12 + .../file8ImportsEmpty.bazelproject | 13 + .../file9ImportsNotExisting.bazelproject | 13 + .../projectview/projectView.bazelproject | 15 - .../projectViewToImport.bazelproject | 12 - .../projectViewWithImport.bazelproject | 7 - .../projectViewWithTabs.bazelproject | 15 - ...projectViewWithoutDirectories.bazelproject | 13 - .../projectViewWithoutTargets.bazelproject | 10 - .../without/bazelpath.bazelproject | 15 + .../without/debuggeraddress.bazelproject | 15 + .../projectview/without/javapath.bazelproject | 15 + .../projectview/without/targets.bazelproject | 12 + .../java/org/jetbrains/bsp/bazel/server/BUILD | 2 + .../bsp/bazel/server/BazelBspServer.java | 23 +- .../bsp/bazel/server/ServerInitializer.java | 28 +- .../bsp/bazel/server/bep/BepServer.java | 46 +- .../bsp/bazel/server/bep/parsers/error/BUILD | 2 +- .../bep/parsers/error/FileDiagnostic.java | 118 +++- .../error/StderrDiagnosticsParser.java | 19 +- .../bsp/BazelBspServerBuildManager.java | 26 +- .../server/bsp/BspImplementationHub.java | 22 +- .../bsp/bazel/server/bsp/config/BUILD | 2 +- .../config/ServerArgsProjectViewProvider.java | 43 +- .../server/bsp/impl/JvmBuildServerImpl.java | 37 ++ .../bsp/managers/BazelBspAspectsManager.java | 43 +- .../managers/BazelBspJvmTargetManager.java | 11 +- .../bsp/managers/BazelBspQueryManager.java | 21 +- .../managers/BazelBspScalaTargetManager.java | 3 +- .../bsp/managers/BazelCppTargetManager.java | 8 +- .../TargetsLanguageOptionsResolver.java | 10 +- .../server/bsp/resolvers/TargetsUtils.java | 12 +- .../bsp/services/BuildServerService.java | 60 +- .../bsp/services/CppBuildServerService.java | 29 +- .../bsp/services/JvmBuildServerService.java | 85 +++ .../bsp/utils/InternalAspectsResolver.java | 21 + .../server/bsp/utils/SourceRootGuesser.java | 45 ++ .../bsp/bazel/server/bep/parsers/error/BUILD | 29 + .../bep/parsers/error/FileDiagnosticTest.java | 197 ++++++ .../error/StderrDiagnosticsParserTest.java | 322 ++++++++++ .../bsp/bazel/server/bsp/utils/BUILD | 26 + .../utils/InternalAspectsResolverTest.java | 55 ++ .../bsp/utils/SourceRootGuesserTest.java | 51 ++ third_party.bzl | 1 + 136 files changed, 4946 insertions(+), 1231 deletions(-) create mode 100644 .bazelversion create mode 100644 commons/src/main/java/org/jetbrains/bsp/bazel/commons/BetterFiles.java create mode 100644 commons/src/test/java/org/jetbrains/bsp/bazel/commons/BUILD create mode 100644 commons/src/test/java/org/jetbrains/bsp/bazel/commons/BetterFilesTest.java create mode 100644 install/src/main/java/org/jetbrains/bsp/bazel/install/default-projectview.bazelproject create mode 100644 projectview/README.md create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewBazelPathSection.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewDebuggerAddressSection.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewJavaPathSection.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSection.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSectionHeader.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSection.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewTargetsSection.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/BUILD delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/DirectoriesSection.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/TargetsSection.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewImportParser.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewRawSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewBazelPathSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewDebuggerAddressSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewJavaPathSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParser.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParser.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParser.java create mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSections.java delete mode 100644 projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionHeaderPosition.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/BUILD create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewTest.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSectionTest.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSectionTest.java rename projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/{specific => }/BUILD (51%) create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParserTest.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParserTest.java delete mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParserTest.java delete mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParserTest.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionTest.java create mode 100644 projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionsTest.java rename projectview/src/test/resources/{projectview => }/BUILD (71%) create mode 100644 projectview/src/test/resources/projectview/empty.bazelproject create mode 100644 projectview/src/test/resources/projectview/file1.bazelproject create mode 100644 projectview/src/test/resources/projectview/file2.bazelproject create mode 100644 projectview/src/test/resources/projectview/file3.bazelproject create mode 100644 projectview/src/test/resources/projectview/file4ImportsFile1.bazelproject create mode 100644 projectview/src/test/resources/projectview/file5ImportsFile1File2File3.bazelproject create mode 100644 projectview/src/test/resources/projectview/file6ImportsFile2File3File4.bazelproject create mode 100644 projectview/src/test/resources/projectview/file7ImportsFile1.bazelproject create mode 100644 projectview/src/test/resources/projectview/file8ImportsEmpty.bazelproject create mode 100644 projectview/src/test/resources/projectview/file9ImportsNotExisting.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectView.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectViewToImport.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectViewWithImport.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectViewWithTabs.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectViewWithoutDirectories.bazelproject delete mode 100644 projectview/src/test/resources/projectview/projectViewWithoutTargets.bazelproject create mode 100644 projectview/src/test/resources/projectview/without/bazelpath.bazelproject create mode 100644 projectview/src/test/resources/projectview/without/debuggeraddress.bazelproject create mode 100644 projectview/src/test/resources/projectview/without/javapath.bazelproject create mode 100644 projectview/src/test/resources/projectview/without/targets.bazelproject create mode 100644 server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/impl/JvmBuildServerImpl.java create mode 100644 server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/JvmBuildServerService.java create mode 100644 server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolver.java create mode 100644 server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesser.java create mode 100644 server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD create mode 100644 server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnosticTest.java create mode 100644 server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParserTest.java create mode 100644 server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolverTest.java create mode 100644 server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesserTest.java diff --git a/.bazelrc b/.bazelrc index 235f9bfc3..e1f09e07b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,5 +1,5 @@ -build --java_toolchain=@bazel_tools//tools/jdk:toolchain_java8 -build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java8 +build --java_toolchain=@bazel_tools//tools/jdk:toolchain_java11 +build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java11 build --javacopt="-Xep:WildcardImport:ERROR" test --javacopt="-Xep:WildcardImport:ERROR" run --javacopt="-Xep:WildcardImport:ERROR" diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000..0b2eb36f5 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +3.7.2 diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index a1bb73c17..e9ea2a913 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -30,10 +30,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -49,10 +49,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -68,10 +68,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -87,10 +87,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -106,10 +106,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -125,10 +125,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -144,10 +144,10 @@ jobs: needs: setup steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Cache bazel uses: actions/cache@v2 env: @@ -156,4 +156,4 @@ jobs: path: ~/.cache/bazel key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.sha }} - name: Run test - run: bazel run //e2e:BazelBspEntireRepositoryImportTest \ No newline at end of file + run: bazel run //e2e:BazelBspEntireRepositoryImportTest diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 81c7d5b99..9f92b057a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -7,25 +7,36 @@ on: branches: [ dev, master ] jobs: + commons-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Run tests + run: bazel test //commons/... + bazelrunner-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Run tests - run: bazel test //bazelrunner/... + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Run tests + run: bazel test //bazelrunner/... projectview-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Run tests run: bazel test //projectview/... @@ -33,9 +44,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: Run tests run: bazel test //server/... diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a618d123..7b3debbe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,29 +2,38 @@ ## [Unreleased] +### Features 🎉 +- New, better project view! Now you can configure installer as well! | [#143](https://github.com/JetBrains/bazel-bsp/pull/143) + +### Changes 🔄 +- Improved heuristics for guessing source roots. It looks directory structures such as `src/java` or `main/java`. | [#126](https://github.com/JetBrains/bazel-bsp/pull/126) + +### Fixes 🛠️ +- Error diagnostics are now also sent for source files, including targets. | [#146](https://github.com/JetBrains/bazel-bsp/pull/146) +- Now the project is built using bazel version `3.7.2`, as the rules currently used are no longer supported by bazel. | [#141](https://github.com/JetBrains/bazel-bsp/pull/141) + ## [1.0.1] - 24.09.2021 -### Added +### Features 🎉 - [This](CHANGELOG.md) changelog. | [#97](https://github.com/JetBrains/bazel-bsp/pull/97) -### Changed +### Changes 🔄 - Implementation of e2e tests. Have been moved to the `e2e` module and are now based on execution scenarios. | [#83](https://github.com/JetBrains/bazel-bsp/pull/83) - Github actions e2e tests execution - e2e tests are executed in parallel. | [#84](https://github.com/JetBrains/bazel-bsp/pull/84) - Project structure - now it is multi module project. | [#87](https://github.com/JetBrains/bazel-bsp/pull/87) - Github actions unit tests execution - each module has a separate job. | [#89](https://github.com/JetBrains/bazel-bsp/pull/89) - Created [document](docs/BUMPVERSION.md) with release tips. | [#91](https://github.com/JetBrains/bazel-bsp/pull/91) -### Removed +### Removed ✂️ - Old implementation of e2e tests. | [#86](https://github.com/JetBrains/bazel-bsp/pull/86) -### Fixed +### Fixes 🛠️ - Warnings generated by the [WORKSPACE file](WORKSPACE). | [#81](https://github.com/JetBrains/bazel-bsp/pull/81) - Semantic versioning parser - now it can parse every valid version. | [#93](https://github.com/JetBrains/bazel-bsp/pull/93) - `exports` attribute propagation to the BSP | [#98](https://github.com/JetBrains/bazel-bsp/pull/98) - Now all `scala_junit_test` based rules (including `scala_specs2_junit_test`) are included in the BSP tests, unfortunately without test classes. | [#101](https://github.com/JetBrains/bazel-bsp/pull/101) ## [1.0.0] - 23.08.2021 -### Added - Everything... diff --git a/README.md b/README.md index 2d2d07784..980f5425a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![JetBrains team project](http://jb.gg/badges/team.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) -[![Integration test](https://github.com/JetBrains/bazel-bsp/actions/workflows/integration-test.yml/badge.svg)](https://github.com/JetBrains/bazel-bsp/actions/workflows/integration-test.yml) +[![Format](https://github.com/JetBrains/bazel-bsp/actions/workflows/format.yml/badge.svg)](https://github.com/JetBrains/bazel-bsp/actions/workflows/format.yml) +[![Integration tests](https://github.com/JetBrains/bazel-bsp/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/JetBrains/bazel-bsp/actions/workflows/integration-tests.yml) +[![Unit tests](https://github.com/JetBrains/bazel-bsp/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/JetBrains/bazel-bsp/actions/workflows/unit-tests.yml) # Bazel BSP An implementation of the [Build Server Protocol](https://github.com/build-server-protocol/build-server-protocol) for Bazel. @@ -52,9 +54,9 @@ cs launch -r m2Local org.jetbrains.bsp:bazel-bsp: -M org.jetbrains ## Project Views -In order to work on huge monorepos you might want to specify directories and targets to work on. To address this issue, Bazel BSP supports part of the [Project Views](https://ij.bazel.build/docs/project-views.html) introduced by Google. Currently you can use following rules: `directories`, `targets` and `import`. +In order to work on huge monorepos you might want to specify directories and targets to work on. To address this issue, Bazel BSP supports (partly) the [Project Views](https://ij.bazel.build/docs/project-views.html) introduced by Google. -Simply create a `projectview.bazelproject` file, specify rules inside and run the server. If no such files will be found, by default entire project will be loaded. +Check [project view readme](projectview/README.md) for more info. ## Tests diff --git a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelDataResolver.java b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelDataResolver.java index 12f70fac7..6b63cdeb2 100644 --- a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelDataResolver.java +++ b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelDataResolver.java @@ -27,7 +27,8 @@ public BazelData resolveBazelData() { String version = readOnlyBazelLine(BAZEL_VERSION_PARAMETER); Path workspacePath = Paths.get(execRoot); String workspaceLabel = workspacePath.toFile().getName(); - return new BazelData(execRoot, workspaceRoot, binRoot, workspaceLabel, version); + Path bspProjectRoot = Paths.get("").toAbsolutePath().normalize(); + return new BazelData(execRoot, workspaceRoot, binRoot, workspaceLabel, version, bspProjectRoot); } private String readOnlyBazelLine(String argument) { diff --git a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelRunner.java b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelRunner.java index e546e63c9..a041e482d 100644 --- a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelRunner.java +++ b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/BazelRunner.java @@ -1,9 +1,13 @@ package org.jetbrains.bsp.bazel.bazelrunner; +import ch.epfl.scala.bsp4j.StatusCode; +import com.google.common.base.Suppliers; import com.google.common.collect.Lists; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.bsp.bazel.server.loggers.BuildClientLogger; @@ -19,9 +23,27 @@ public class BazelRunner { private Optional besBackendPort = Optional.empty(); private Optional buildClientLogger = Optional.empty(); + private final Supplier workspaceRoot; public BazelRunner(String bazelBinaryPath) { this.bazel = bazelBinaryPath; + this.workspaceRoot = Suppliers.memoize(this::resolveWorkspaceRoot); + } + + private File resolveWorkspaceRoot() { + var builder = new ProcessBuilder(bazel, "info", "workspace"); + try { + var process = new BazelProcess(builder.start(), Optional.empty()); + var result = process.waitAndGetResult(); + if (result.getStatusCode() != StatusCode.OK) { + throw new RuntimeException( + "Failed to run bazel info workspace. Make sure that the project is created inside a" + + " bazel workspace"); + } + return new File(result.getStdout().get(0)); + } catch (IOException e) { + throw new RuntimeException(e); + } } public BazelRunnerCommandBuilder commandBuilder() { @@ -48,10 +70,6 @@ private String getBesBackendAddress() { } BazelProcess runBazelCommand(String command, List flags, List arguments) { - if (arguments.isEmpty()) { - LOGGER.fatal("Not enough arguments"); - throw new IllegalArgumentException("Not enough arguments"); - } try { LOGGER.info( @@ -68,6 +86,7 @@ private synchronized BazelProcess runBazelProcess( LOGGER.info("Running: {}", processArgs); ProcessBuilder processBuilder = new ProcessBuilder(processArgs); + processBuilder.directory(workspaceRoot.get()); Process process = processBuilder.start(); return new BazelProcess(process, buildClientLogger); diff --git a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/data/BazelData.java b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/data/BazelData.java index f5280e7af..67e8675e1 100644 --- a/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/data/BazelData.java +++ b/bazelrunner/src/main/java/org/jetbrains/bsp/bazel/bazelrunner/data/BazelData.java @@ -1,5 +1,7 @@ package org.jetbrains.bsp.bazel.bazelrunner.data; +import java.nio.file.Path; + public class BazelData { private final String execRoot; @@ -7,18 +9,21 @@ public class BazelData { private final String binRoot; private final String workspaceLabel; private final SemanticVersion version; + private final Path bspProjectRoot; public BazelData( String execRoot, String workspaceRoot, String binRoot, String workspaceLabel, - String version) { + String version, + Path bspProjectRoot) { this.execRoot = execRoot; this.workspaceRoot = workspaceRoot; this.binRoot = binRoot; this.workspaceLabel = workspaceLabel; this.version = SemanticVersion.fromReleaseData(version); + this.bspProjectRoot = bspProjectRoot; } public String getExecRoot() { @@ -40,4 +45,8 @@ public String getWorkspaceLabel() { public SemanticVersion getVersion() { return version; } + + public Path getBspProjectRoot() { + return bspProjectRoot; + } } diff --git a/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BUILD b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BUILD index 815e6e8f1..38e38d2d9 100644 --- a/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BUILD +++ b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BUILD @@ -7,5 +7,6 @@ java_library( deps = [ "@maven//:ch_epfl_scala_bsp4j", "@maven//:com_google_guava_guava", + "@maven//:io_vavr_vavr", ], ) diff --git a/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BetterFiles.java b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BetterFiles.java new file mode 100644 index 000000000..772397434 --- /dev/null +++ b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/BetterFiles.java @@ -0,0 +1,17 @@ +package org.jetbrains.bsp.bazel.commons; + +import com.google.common.io.CharSource; +import com.google.common.io.Files; +import io.vavr.control.Try; +import java.nio.charset.Charset; +import java.nio.file.Path; + +public final class BetterFiles { + + public static Try tryReadFileContent(Path filePath) { + return Try.success(filePath) + .map(Path::toFile) + .map(file -> Files.asCharSource(file, Charset.defaultCharset())) + .mapTry(CharSource::read); + } +} diff --git a/commons/src/main/java/org/jetbrains/bsp/bazel/commons/Constants.java b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/Constants.java index e39887ef5..cc7325fb1 100644 --- a/commons/src/main/java/org/jetbrains/bsp/bazel/commons/Constants.java +++ b/commons/src/main/java/org/jetbrains/bsp/bazel/commons/Constants.java @@ -58,13 +58,15 @@ public class Constants { public static final String BAZELBSP_LOG_FILE_NAME = "bazelbsp.log"; public static final String BAZELBSP_TRACE_JSON_FILE_NAME = "bazelbsp.trace.json"; - public static final List KNOWN_SOURCE_ROOTS = - ImmutableList.of("java", "scala", "kotlin", "javatests", "src", "test", "main", "testsrc"); public static final String DIAGNOSTICS = "diagnostics"; public static final String EXEC_ROOT_PREFIX = "exec-root://"; public static final String SCALA_COMPILER_CLASSPATH_FILES = "scala_compiler_classpath_files"; + public static final String JAVA_RUNTIME_CLASSPATH_ASPECT_OUTPUT_GROUP = + "java_runtime_classpath_files"; public static final String SCALA_TEST_MAIN_CLASSES_ATTRIBUTE_NAME = "main_class"; - public static final String DEFAULT_PROJECT_VIEW_FILE = "projectview.bazelproject"; + public static final String PROJECT_VIEW_FILE_PATH = "projectview.bazelproject"; + public static final String DEFAULT_PROJECT_VIEW_FILE_PATH = + ".bazelbsp/default-projectview.bazelproject"; } diff --git a/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BUILD b/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BUILD new file mode 100644 index 000000000..c123ac243 --- /dev/null +++ b/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BUILD @@ -0,0 +1,14 @@ +load("@rules_java//java:defs.bzl", "java_test") + +java_test( + name = "BetterFilesTest", + size = "small", + srcs = ["BetterFilesTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//commons/src/main/java/org/jetbrains/bsp/bazel/commons", + "@maven//:io_vavr_vavr", + ], +) diff --git a/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BetterFilesTest.java b/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BetterFilesTest.java new file mode 100644 index 000000000..daf32634a --- /dev/null +++ b/commons/src/test/java/org/jetbrains/bsp/bazel/commons/BetterFilesTest.java @@ -0,0 +1,45 @@ +package org.jetbrains.bsp.bazel.commons; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Paths; +import org.junit.Test; + +public class BetterFilesTest { + + @Test + public void shouldReturnFailureForNotExistingFile() { + // given + var filePath = Paths.get("file/doesnt/exist"); + + // when + var fileContent = BetterFiles.tryReadFileContent(filePath); + + // then + assertTrue(fileContent.isFailure()); + } + + @Test + public void shouldParseExistingFile() throws IOException { + // given + var file = File.createTempFile("test", "file"); + file.deleteOnExit(); + var filePath = file.toPath(); + + var fileWriter = new FileWriter(file); + + fileWriter.write("test content"); + fileWriter.close(); + + // when + var fileContent = BetterFiles.tryReadFileContent(filePath); + + // then + assertTrue(fileContent.isSuccess()); + assertEquals("test content", fileContent.get()); + } +} diff --git a/docs/BUMPVERSION.md b/docs/BUMPVERSION.md index 4f244be26..26cfa501b 100644 --- a/docs/BUMPVERSION.md +++ b/docs/BUMPVERSION.md @@ -1,5 +1,7 @@ # Bump Version + If you want to bump version (for example, before a release) you should do this in the following places: + - `central-sync/VERSION` - `maven_coordinates` in `server/src/main/java/org/jetbrains/bsp/bazel/BUILD` - `VERSION` in `commons/src/main/java/org/jetbrains/bsp/bazel/commons/Constants.java` diff --git a/docs/kotlin.md b/docs/kotlin.md index d58395aa8..c844700d3 100644 --- a/docs/kotlin.md +++ b/docs/kotlin.md @@ -1,6 +1,9 @@ # BSP Kotlin -In order to fully have the experience to use BSP over kotlin, make sure that you're using at least version `0da862aaa11db26f6a113dcec5f6828bfd186ac9` of `rules_kotlin`. This will ensure + +In order to fully have the experience to use BSP over kotlin, make sure that you're using at least +version `0da862aaa11db26f6a113dcec5f6828bfd186ac9` of `rules_kotlin`. This will ensure that compilation diagnostics are being delivered to you. + ``` rules_kotlin_version = "0da862aaa11db26f6a113dcec5f6828bfd186ac9" rules_kotlin_sha = "6fda2451b9aaf78a8399e6e5d13c31c8ddc558e87de209a7cfd5ddc777ac7877" @@ -15,4 +18,5 @@ kotlin_repositories() # if you want the default kt_register_toolchains() # to use the default toolchain ``` -This changes have not been merged yet and progress can be accompanied [here](https://github.com/bazelbuild/rules_kotlin/pull/359). \ No newline at end of file +This changes have not been merged yet and progress can be +accompanied [here](https://github.com/bazelbuild/rules_kotlin/pull/359). \ No newline at end of file diff --git a/docs/scala.md b/docs/scala.md index ceab866bc..7976f6d16 100644 --- a/docs/scala.md +++ b/docs/scala.md @@ -1,6 +1,8 @@ # Toolchain Registration for Scala + In order to fully use Bazel BSP for Scala projects make sure to use at least version - ``d8bce245a96ca9ab721324bc8daa984aa13fa0f7`` of `rules_scala`: +`d8bce245a96ca9ab721324bc8daa984aa13fa0f7` of `rules_scala`: + ``` http_archive( name = "io_bazel_rules_scala", @@ -13,7 +15,9 @@ bazel_version(name = "bazel_version") ``` Make sure that your registered toolchain has Compilation Diagnostics enabled: + - At a BUILD file: + ``` load("@io_bazel_rules_scala//scala:scala_toolchain.bzl", "scala_toolchain") @@ -30,7 +34,9 @@ toolchain( visibility = ["//visibility:public"], ) ``` + - At the WORKSPACE file: + ``` register_toolchains( ":diagnostics_reporter_toolchain" diff --git a/e2e/runTest.sh b/e2e/runTest.sh index 85d923acc..29f6ad9ae 100755 --- a/e2e/runTest.sh +++ b/e2e/runTest.sh @@ -36,11 +36,16 @@ fi bsp_path="$(bazel info bazel-bin)/server/src/main/java/org/jetbrains/bsp/bazel/bsp-install" echo "Building done." -echo "Installing BSP..." +echo "Cleaning project directory..." if [ "$#" -eq 2 ]; then cd "$TEST_PROJECT_PATH" || exit fi -$bsp_path +rm -r .bsp/ > /dev/null 2>&1 +rm -r .bazelbsp/ > /dev/null 2>&1 +echo "Cleaning project directory done!" + +echo "Installing BSP..." +$bsp_path || exit echo "Installing done." echo "Environment has been prepared!" echo -e "-----------------------------------\n" diff --git a/e2e/src/main/java/org/jetbrains/bsp/bazel/BazelBspSampleRepoTest.java b/e2e/src/main/java/org/jetbrains/bsp/bazel/BazelBspSampleRepoTest.java index 371321555..78d5ed04a 100644 --- a/e2e/src/main/java/org/jetbrains/bsp/bazel/BazelBspSampleRepoTest.java +++ b/e2e/src/main/java/org/jetbrains/bsp/bazel/BazelBspSampleRepoTest.java @@ -8,6 +8,11 @@ import ch.epfl.scala.bsp4j.DependencySourcesResult; import ch.epfl.scala.bsp4j.InverseSourcesResult; import ch.epfl.scala.bsp4j.JvmBuildTarget; +import ch.epfl.scala.bsp4j.JvmEnvironmentItem; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentResult; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentResult; import ch.epfl.scala.bsp4j.ResourcesItem; import ch.epfl.scala.bsp4j.ResourcesResult; import ch.epfl.scala.bsp4j.ScalaBuildTarget; @@ -44,7 +49,7 @@ public BazelBspSampleRepoTest() { // we cannot use `bazel test ...` because test runner blocks bazel daemon, // but testing server needs it for queries and etc public static void main(String[] args) { - BazelBspSampleRepoTest test = new BazelBspSampleRepoTest(); + var test = new BazelBspSampleRepoTest(); test.executeScenario(); } @@ -58,7 +63,9 @@ protected List getScenarioSteps() { inverseSourcesResults(), dependencySourcesResults(), scalaMainClasses(), - scalaTestClasses() + scalaTestClasses(), + jvmRunEnvironment(), + jvmTestEnvironment() // TODO // new BazelBspServerSingleTest( // "targets run unsuccessfully", @@ -264,6 +271,59 @@ private BazelBspTestScenarioStep dependencySourcesResults() { expectedWorkspaceBuildTargetsResult, expectedDependencies)); } + private BazelBspTestScenarioStep jvmRunEnvironment() { + JvmRunEnvironmentParams params = + new JvmRunEnvironmentParams( + ImmutableList.of(new BuildTargetIdentifier("//example:example"))); + + JvmRunEnvironmentResult expectedResult = + new JvmRunEnvironmentResult( + ImmutableList.of( + new JvmEnvironmentItem( + new BuildTargetIdentifier("//example:example"), + ImmutableList.of( + "/dep/dep.jar", + "/dep/dep_java.jar", + "/dep/deeper/deeper.jar", + "/example/example.jar", + "/scala-library-2.12.8.jar", + "/https/repo1.maven.org/maven2/com/google/guava/guava/28.0-jre/guava-28.0-jre.jar"), + ImmutableList.of("-Xms2G -Xmx5G"), + "/e2e/test-resources/sample-repo", + System.getenv()))); + + return new BazelBspTestScenarioStep( + "jvm run environment results", + () -> testClient.testJvmRunEnvironment(params, expectedResult)); + } + + private BazelBspTestScenarioStep jvmTestEnvironment() { + JvmTestEnvironmentParams params = + new JvmTestEnvironmentParams( + ImmutableList.of(new BuildTargetIdentifier("//example:example-test"))); + + JvmTestEnvironmentResult expectedResult = + new JvmTestEnvironmentResult( + ImmutableList.of( + new JvmEnvironmentItem( + new BuildTargetIdentifier("//example:example-test"), + ImmutableList.of( + "/scala-library-2.12.8.jar", + "/scalatest_2.12-3.0.5.jar", + "/example/example-test.jar", + "/dep/dep.jar", + "/dep/dep_java.jar", + "/dep/deeper/deeper.jar", + "/https/repo1.maven.org/maven2/com/google/guava/guava/28.0-jre/guava-28.0-jre.jar"), + ImmutableList.of(), + "/e2e/test-resources/sample-repo", + System.getenv()))); + + return new BazelBspTestScenarioStep( + "jvm test environment results", + () -> testClient.testJvmTestEnvironment(params, expectedResult)); + } + private WorkspaceBuildTargetsResult getExpectedWorkspaceBuildTargetsResult() { List scalaTargetsJars = ImmutableList.of( diff --git a/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestBaseScenario.java b/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestBaseScenario.java index 2305843ae..02a3401e3 100644 --- a/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestBaseScenario.java +++ b/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestBaseScenario.java @@ -60,7 +60,11 @@ public void executeScenario() { } private boolean executeScenarioSteps() { - return getScenarioSteps().stream().allMatch(BazelBspTestScenarioStep::executeAndReturnResult); + return getScenarioSteps().stream() + .map(BazelBspTestScenarioStep::executeAndReturnResult) + .collect(java.util.stream.Collectors.toList()) + .stream() + .allMatch(x -> x); } protected abstract List getScenarioSteps(); diff --git a/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestScenarioStep.java b/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestScenarioStep.java index 6ddb69f43..466fec8ab 100644 --- a/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestScenarioStep.java +++ b/e2e/src/main/java/org/jetbrains/bsp/bazel/base/BazelBspTestScenarioStep.java @@ -22,7 +22,7 @@ public boolean executeAndReturnResult() { return Try.run(testkitCall) .onSuccess(e -> LOGGER.info("Step \"{}\" executed correctly!", testName)) - .onFailure(e -> LOGGER.error("Step \"{}\" execution failed! Exception: {}", testName, e)) + .onFailure(e -> LOGGER.error("Step \"{}\" execution failed!", testName, e)) .map(i -> true) .getOrElse(false); } diff --git a/install.sh b/install.sh index 65f521730..48c87973e 100755 --- a/install.sh +++ b/install.sh @@ -14,6 +14,10 @@ echo -e "Building done." echo -e "\nInstalling server at '$project_path' ..." cd "$project_path" || { echo "cd $project_path failed! EXITING"; exit 155; } + +rm -r .bsp/ +rm -r .bazelbsp/ + $bsp_path echo -e "\nDone! Enjoy Bazel BSP!" diff --git a/install/src/main/java/org/jetbrains/bsp/bazel/install/BUILD b/install/src/main/java/org/jetbrains/bsp/bazel/install/BUILD index ffcb6e6d4..ca4f993a5 100644 --- a/install/src/main/java/org/jetbrains/bsp/bazel/install/BUILD +++ b/install/src/main/java/org/jetbrains/bsp/bazel/install/BUILD @@ -3,10 +3,14 @@ load("@rules_java//java:defs.bzl", "java_library") java_library( name = "install", srcs = glob(["*.java"]), - resources = ["aspects.bzl"], + resources = [ + "aspects.bzl", + "default-projectview.bazelproject", + ], visibility = ["//install:__subpackages__"], deps = [ "//commons", + "//projectview:parser", "@maven//:ch_epfl_scala_bsp4j", "@maven//:com_google_code_gson_gson", "@maven//:com_google_guava_guava", diff --git a/install/src/main/java/org/jetbrains/bsp/bazel/install/Install.java b/install/src/main/java/org/jetbrains/bsp/bazel/install/Install.java index 6ba099c6e..25808f6a0 100644 --- a/install/src/main/java/org/jetbrains/bsp/bazel/install/Install.java +++ b/install/src/main/java/org/jetbrains/bsp/bazel/install/Install.java @@ -24,6 +24,8 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.jetbrains.bsp.bazel.commons.Constants; +import org.jetbrains.bsp.bazel.projectview.model.ProjectView; +import org.jetbrains.bsp.bazel.projectview.parser.ProjectViewDefaultParserProvider; public class Install { @@ -53,25 +55,40 @@ public static void main(String[] args) throws IOException { if (cmd.hasOption(HELP_SHORT_OPT)) { formatter.printHelp(INSTALLER_BINARY_NAME, cliOptions); } else { - addJavaBinary(cmd, argv); + Path rootDir = getRootDir(cmd); + Path bazelbspDir = createDir(rootDir, Constants.BAZELBSP_DIR_NAME); + + Path defaultProjectViewFilePath = bazelbspDir.resolve("default-projectview.bazelproject"); + Files.copy( + Install.class.getResourceAsStream("default-projectview.bazelproject"), + defaultProjectViewFilePath, + StandardCopyOption.REPLACE_EXISTING); + + copyAspects(bazelbspDir); + createEmptyBuildFile(bazelbspDir); + + var projectViewProvider = new ProjectViewDefaultParserProvider(rootDir); + // will be handled properly + var projectView = projectViewProvider.create().get(); + + addJavaBinary(cmd, argv, projectView); addJavaClasspath(argv); - addDebuggerConnection(cmd, argv); + addDebuggerConnection(cmd, argv, projectView); argv.add(SERVER_CLASS_NAME); - addBazelBinary(cmd, argv); + addBazelBinary(cmd, argv, projectView); addBazelTargets(cmd, argv); BspConnectionDetails details = createBspConnectionDetails(argv); - Path rootDir = getRootDir(cmd); writeConfigurationFiles(rootDir, details); - String currentDirectory = getCurrentDirectory(); - System.out.println("Bazel BSP server installed in '" + currentDirectory + "'."); + System.out.println( + "Bazel BSP server installed in '" + rootDir.toAbsolutePath().normalize() + "'."); } } catch (ParseException e) { writer.println(e.getMessage()); formatter.printUsage(writer, 120, INSTALLER_BINARY_NAME, cliOptions); hasError = true; - } catch (NoSuchElementException e) { + } catch (NoSuchElementException | IllegalStateException e) { writer.println(e.getMessage()); hasError = true; } finally { @@ -98,17 +115,21 @@ private static BspConnectionDetails createBspConnectionDetails(List argv Constants.SUPPORTED_LANGUAGES); } - private static void addBazelBinary(CommandLine cmd, List argv) { + private static void addBazelBinary(CommandLine cmd, List argv, ProjectView projectView) { if (cmd.hasOption(BAZEL_SHORT_OPT)) { argv.add(cmd.getOptionValue(BAZEL_SHORT_OPT)); + } else if (projectView.getBazelPath().isPresent()) { + argv.add(projectView.getBazelPath().get().getValue()); } else { argv.add(findOnPath("bazel")); } } - private static void addJavaBinary(CommandLine cmd, List argv) { + private static void addJavaBinary(CommandLine cmd, List argv, ProjectView projectView) { if (cmd.hasOption(JAVA_SHORT_OPT)) { argv.add(cmd.getOptionValue(JAVA_SHORT_OPT)); + } else if (projectView.getJavaPath().isPresent()) { + argv.add(projectView.getJavaPath().get().getValue()); } else { String javaHome = readSystemProperty("java.home"); argv.add(Paths.get(javaHome).resolve("bin").resolve("java").toString()); @@ -126,9 +147,13 @@ private static void addJavaClasspath(List argv) { argv.add(classpath); } - private static void addDebuggerConnection(CommandLine cmd, List argv) { + private static void addDebuggerConnection( + CommandLine cmd, List argv, ProjectView projectView) { if (cmd.hasOption(DEBUGGER_SHORT_OPT)) { - String debuggerAddress = cmd.getOptionValue(DEBUGGER_SHORT_OPT); + var debuggerAddress = cmd.getOptionValue(DEBUGGER_SHORT_OPT); + argv.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + debuggerAddress); + } else if (projectView.getDebuggerAddress().isPresent()) { + var debuggerAddress = projectView.getDebuggerAddress().get().getValue(); argv.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + debuggerAddress); } } @@ -175,10 +200,6 @@ private static void writeBazelbspJson(Path bspDir, Object discoveryDetails) thro private static void writeConfigurationFiles(Path rootDir, Object discoveryDetails) throws IOException { - Path bazelbspDir = createDir(rootDir, Constants.BAZELBSP_DIR_NAME); - copyAspects(bazelbspDir); - createEmptyBuildFile(bazelbspDir); - Path bspDir = createDir(rootDir, Constants.BSP_DIR_NAME); writeBazelbspJson(bspDir, discoveryDetails); } @@ -191,7 +212,9 @@ private static Options getCliOptions() { .longOpt("java") .hasArg() .argName("path") - .desc("Use provided Java executable to run the BSP server") + .desc( + "Deprecated! Use project view file instead. " + + "(Use provided Java executable to run the BSP server)") .build(); cliOptions.addOption(java); @@ -201,7 +224,9 @@ private static Options getCliOptions() { .longOpt("bazel") .hasArg() .argName("path") - .desc("Make the BSP server use this Bazel executable") + .desc( + "Deprecated! Use project view file instead. " + + "(Make the BSP server use this Bazel executable)") .build(); cliOptions.addOption(bazel); @@ -211,7 +236,7 @@ private static Options getCliOptions() { .longOpt("debugger") .hasArg() .argName("address (e.g. '127.0.0.1:8000'") - .desc("Allow BSP server debugging") + .desc("Deprecated! Use project view file instead. (Allow BSP server debugging)") .build(); cliOptions.addOption(debug); @@ -222,8 +247,9 @@ private static Options getCliOptions() { .hasArg() .argName("path") .desc( - "Path to directory where bazelbsp server should be setup. " - + "Current directory will be used by default") + "Deprecated! Use project view file instead. " + + "(Path to directory where bazelbsp server should be setup. " + + "Current directory will be used by default)") .build(); cliOptions.addOption(directory); @@ -234,8 +260,9 @@ private static Options getCliOptions() { .hasArg() .argName("targets") .desc( - "Name of the bazel's targets that the server should import. Targets can be" - + "separated by a comma. The default is to import all targets (//...)") + "Deprecated! Use project view file instead. (Name of the bazel's targets that the" + + " server should import. Targets can be separated by a comma. The default is" + + " to import all targets (//...))") .build(); cliOptions.addOption(targets); @@ -265,8 +292,4 @@ private static String readSystemProperty(String name) { } return property; } - - private static String getCurrentDirectory() { - return System.getProperty("user.dir"); - } } diff --git a/install/src/main/java/org/jetbrains/bsp/bazel/install/aspects.bzl b/install/src/main/java/org/jetbrains/bsp/bazel/install/aspects.bzl index fed768889..3bde3a4ed 100644 --- a/install/src/main/java/org/jetbrains/bsp/bazel/install/aspects.bzl +++ b/install/src/main/java/org/jetbrains/bsp/bazel/install/aspects.bzl @@ -29,6 +29,23 @@ scala_compiler_classpath_aspect = aspect( implementation = _scala_compiler_classpath_impl, ) +def _java_runtime_classpath_impl(target, ctx): + files = depset() + if JavaInfo in target: + java_info = target[JavaInfo] + files = java_info.compilation_info.runtime_classpath if java_info.compilation_info else java_info.transitive_runtime_jars + + output_file = ctx.actions.declare_file("%s-runtime_classpath.textproto" % target.label.name) + ctx.actions.write(output_file, struct(files = [file.path for file in files.to_list()]).to_proto()) + + return [ + OutputGroupInfo(java_runtime_classpath_files = [output_file]), + ] + +java_runtime_classpath_aspect = aspect( + implementation = _java_runtime_classpath_impl, +) + def _fetch_cpp_compiler(target, ctx): if cc_common.CcToolchainInfo in target: toolchain_info = target[cc_common.CcToolchainInfo] diff --git a/install/src/main/java/org/jetbrains/bsp/bazel/install/default-projectview.bazelproject b/install/src/main/java/org/jetbrains/bsp/bazel/install/default-projectview.bazelproject new file mode 100644 index 000000000..2dced3f6b --- /dev/null +++ b/install/src/main/java/org/jetbrains/bsp/bazel/install/default-projectview.bazelproject @@ -0,0 +1,5 @@ +# this is default project configuration + +# all targets in the project are included +targets: + //... diff --git a/pom.xml b/pom.xml index 6d8452f7e..3a336f8bd 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,8 @@ Bazel BSP - An implementation of the Build Server Protocol for Bazel. Allows clients such as IntelliJ to obtain build specific + An implementation of the Build Server Protocol for Bazel. Allows clients such as IntelliJ to obtain build + specific information in order to improve the experience of users in the IDE. https://www.jetbrains.com/ diff --git a/projectview/BUILD b/projectview/BUILD index 6aa22d253..327aed0fe 100644 --- a/projectview/BUILD +++ b/projectview/BUILD @@ -5,8 +5,6 @@ java_library( visibility = ["//visibility:public"], exports = [ "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific", ], ) @@ -14,6 +12,7 @@ java_library( name = "parser", visibility = ["//visibility:public"], exports = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser", ], ) diff --git a/projectview/README.md b/projectview/README.md new file mode 100644 index 000000000..42a3febae --- /dev/null +++ b/projectview/README.md @@ -0,0 +1,247 @@ +# Project View + +The project view file (*.bazelproject) is used to import a subset of bazel targets into the IDE, configure a project, +and specify how the bsp server will be started. + +This is our adaptation of project view know +from [Bazel Plugin for Intellij](https://ij.bazel.build/docs/project-views.html) + +> The project view file uses a python-like format with 2 spaces indentation and # comments. You can share the +> *.bazelproject file between projects, use your own copy, or both. +> +> In general, you can start with just ~~directories and~~ targets and add more sections as you want to further tweak +> your IDE workspace. + + +**Note:** Each section has a default value -- some (e.g. `targets`) are located +in `.bazelbsp/default-projectview.bazelproject`, some (e.g. `bazel_path`) are deducted at the runtime. + +## Usage + +**Note:** We will be changing this mechanism in future releases. + +Simply put `projectview.bazelproject` file in the root of your project and fill it. + +_Remember that you can always find the default values in `.bazelbsp/default-projectview.bazelproject`._ + +## Available sections + +--- + +#### import + +Imports another project view. + +You may use multiple imports in any project view. Any list type sections (e.g. `targets`) compose. Single-value +sections (e.g. `bazel_path`) override and use the last one encountered, depth-first parse order (i.e. imports are +evaluated as they are encountered). + +##### example: + +``` +import path/to/another/projectview.bazelproject +``` + +--- + +### Server installer sections + +These sections are read during server [installation](https://github.com/JetBrains/bazel-bsp#installation) +-- the moment when `.bsp/` is created containing all the necessary information to start the server. + +**Note**: It happens when the installer is invoked -- **before** starting the server! + +--- + +#### java_path + +Path to java which will be used to start the server (first argument in `argv` in `.bsp/bazelbsp.json`). + +##### example: + +``` +java_path: /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home/bin/java +``` + +##### default: + +The following code will be used to deduct a java path automatically: + +``` +System.getProperty("java.home").resolve("bin").resolve("java") +``` + +--- + +#### debugger_address + +Address of debugger which will be attached to the java program by the flag: + +``` + -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=` +``` + +##### example: + +``` +debugger_address: 127.0.0.1:8000 +``` + +##### default: + +The server will be started without debugger + +--- + +### Project sections + +These sections are read when server is starting -- usually when you open a project in IDE. + +--- + +#### targets + +A list of bazel target expressions, they support `/...` notation. + +Targets are built during the server lifetime, so the more targets you have, the slower your IDE experience might be. You +can +use negative targets to have server ignore certain targets ( +e.g. `-//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/...`). + +##### example: + +``` +targets: + //install/src/main/java/org/jetbrains/bsp/bazel/install + //projectview/... + -//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/... +``` + +##### default: + +All targets are included: + +``` +targets: + //... +``` + +--- + +#### bazel_path + +Path to bazel which will be used to invoke bazel from the server (e.g. to build a project, or query bazel). + +##### example: + +``` +bazel_path: /usr/local/bin/bazel +``` + +##### default: + +The server will deduct bazel path from `$PATH` + +--- + +#### directories + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### derive_targets_from_directories + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### workspace_type + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### additional_languages + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### java_language_level + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### test_sources + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### shard_sync + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### target_shard_size + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### exclude_library + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### build_flags + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### sync_flags + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### test_flags + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### import_run_configurations + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### android_sdk_platform + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### android_min_sdk + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### generated_android_resource_directories + +_We are working on it, you can expect support for this section in future releases._ + +--- + +#### ts_config_rules + +_We are working on it, you can expect support for this section in future releases._ + +--- diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/BUILD index 821e86711..f83d4faf1 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/BUILD +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/BUILD @@ -4,9 +4,14 @@ java_library( name = "model", srcs = glob(["*.java"]), visibility = ["//projectview:__subpackages__"], + exports = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", + "@maven//:io_vavr_vavr", + ], deps = [ + "//commons", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific", "@maven//:com_google_guava_guava", + "@maven//:io_vavr_vavr", ], ) diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectView.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectView.java index 5b8f7337c..0bb85ac4e 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectView.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectView.java @@ -1,90 +1,211 @@ package org.jetbrains.bsp.bazel.projectview.model; +import io.vavr.collection.Seq; +import io.vavr.control.Try; +import java.util.List; import java.util.Objects; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; +import java.util.Optional; +import java.util.function.Function; +import org.jetbrains.bsp.bazel.commons.ListUtils; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDebuggerAddressSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewJavaPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewListSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSingletonSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; /** - * Representation of the project view file + * Representation of the project view file. * * @link https://ij.bazel.build/docs/project-views.html */ public class ProjectView { - private final DirectoriesSection directories; - private final TargetsSection targets; + /** targets included and excluded from the project */ + private final ProjectViewTargetsSection targets; - private ProjectView(DirectoriesSection directories, TargetsSection targets) { - this.directories = directories; + /** bazel path used to invoke bazel from the code */ + private final Optional bazelPath; + + /** debugger address which can be added to the server run command (as a flag to java) */ + private final Optional debuggerAddress; + + /** path to java to run a server */ + private final Optional javaPath; + + private ProjectView( + ProjectViewTargetsSection targets, + Optional bazelPath, + Optional debuggerAddress, + Optional javaPath) { this.targets = targets; + this.bazelPath = bazelPath; + this.debuggerAddress = debuggerAddress; + this.javaPath = javaPath; } public static ProjectView.Builder builder() { return new Builder(); } - public ProjectView merge(ProjectView projectView) { - return ProjectView.builder() - .directories(directories.merge(projectView.directories)) - .targets(targets.merge(projectView.targets)) - .build(); + public ProjectViewTargetsSection getTargets() { + return targets; + } + + public Optional getBazelPath() { + return bazelPath; } - public DirectoriesSection getDirectories() { - return directories; + public Optional getDebuggerAddress() { + return debuggerAddress; } - public TargetsSection getTargets() { - return targets; + public Optional getJavaPath() { + return javaPath; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProjectView)) return false; + ProjectView that = (ProjectView) o; + return targets.equals(that.targets) + && bazelPath.equals(that.bazelPath) + && debuggerAddress.equals(that.debuggerAddress) + && javaPath.equals(that.javaPath); + } + + @Override + public int hashCode() { + return Objects.hash(targets, bazelPath, debuggerAddress, javaPath); + } + + @Override + public String toString() { + return "ProjectView{" + + "targets=" + + targets + + ", bazelPath=" + + bazelPath + + ", debuggerAddress=" + + debuggerAddress + + ", javaPath=" + + javaPath + + '}'; } public static class Builder { - private DirectoriesSection directories; - private TargetsSection targets; + private List> importedProjectViews = List.of(); + + private ProjectViewTargetsSection targets = new ProjectViewTargetsSection(); - public Builder directories(DirectoriesSection directories) { - this.directories = directories; + private Optional bazelPath = Optional.empty(); + + private Optional debuggerAddress = Optional.empty(); + + private Optional javaPath = Optional.empty(); + + private Builder() {} + + public Builder imports(List> importedProjectViews) { + this.importedProjectViews = importedProjectViews; return this; } - public Builder targets(TargetsSection target) { + public Builder targets(ProjectViewTargetsSection target) { this.targets = target; return this; } - public ProjectView build() { - assertRequiredFields(); + public Builder bazelPath(Optional bazelPath) { + this.bazelPath = bazelPath; + return this; + } - return new ProjectView(directories, targets); + public Builder debuggerAddress(Optional debuggerAddress) { + this.debuggerAddress = debuggerAddress; + return this; } - private void assertRequiredFields() { - if (directories == null) { - throw new IllegalStateException("directories section is required!"); - } + public Builder javaPath(Optional javaPath) { + this.javaPath = javaPath; + return this; + } - if (targets == null) { - throw new IllegalStateException("targets section is required!"); - } + public Try build() { + return Try.sequence(importedProjectViews).map(Seq::toJavaList).map(this::buildWithImports); } - } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ProjectView that = (ProjectView) o; - return Objects.equals(directories, that.directories) && Objects.equals(targets, that.targets); - } + private ProjectView buildWithImports(List importedProjectViews) { + var targets = combineTargetsSection(importedProjectViews); + var bazelPath = combineBazelPathSection(importedProjectViews); + var debuggerAddress = combineDebuggerAddressSection(importedProjectViews); + var javaPath = combineJavaPathSection(importedProjectViews); - @Override - public int hashCode() { - return Objects.hash(directories, targets); - } + return new ProjectView(targets, bazelPath, debuggerAddress, javaPath); + } - @Override - public String toString() { - return "ProjectView{" + "directories=" + directories + ", targets=" + targets + '}'; + private ProjectViewTargetsSection combineTargetsSection( + List importedProjectViews) { + var includedTargets = + combineListValuesWithImported( + importedProjectViews, + targets, + ProjectView::getTargets, + ProjectViewListSection::getIncludedValues); + var excludedTargets = + combineListValuesWithImported( + importedProjectViews, + targets, + ProjectView::getTargets, + ProjectViewListSection::getExcludedValues); + + return new ProjectViewTargetsSection(includedTargets, excludedTargets); + } + + private List combineListValuesWithImported( + List importedProjectViews, + T section, + Function sectionGetter, + Function> valuesGetter) { + return importedProjectViews.stream() + .map(sectionGetter) + .map(valuesGetter) + .reduce(valuesGetter.apply(section), ListUtils::concat); + } + + private Optional combineBazelPathSection( + List importedProjectViews) { + var defaultBazelPathSection = + getLastImportedSingletonValue(importedProjectViews, ProjectView::getBazelPath); + + return bazelPath.or(() -> defaultBazelPathSection); + } + + private Optional combineDebuggerAddressSection( + List importedProjectViews) { + var defaultDebuggerAddressSection = + getLastImportedSingletonValue(importedProjectViews, ProjectView::getDebuggerAddress); + + return debuggerAddress.or(() -> defaultDebuggerAddressSection); + } + + private Optional combineJavaPathSection( + List importedProjectViews) { + var defaultJavaPathSection = + getLastImportedSingletonValue(importedProjectViews, ProjectView::getJavaPath); + + return javaPath.or(() -> defaultJavaPathSection); + } + + private Optional getLastImportedSingletonValue( + List importedProjectViews, Function> sectionGetter) { + return importedProjectViews.stream() + .map(sectionGetter) + .filter(Optional::isPresent) + .map(Optional::get) + .reduce((first, second) -> second); + } } } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewDefaultProvider.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewDefaultProvider.java index 80e3ffcfd..e69de29bb 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewDefaultProvider.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewDefaultProvider.java @@ -1,19 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.model; - -import com.google.common.collect.ImmutableList; -import java.nio.file.Paths; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; - -public class ProjectViewDefaultProvider implements ProjectViewProvider { - - @Override - public ProjectView create() { - DirectoriesSection directoriesSection = - new DirectoriesSection(ImmutableList.of(Paths.get(".")), ImmutableList.of()); - TargetsSection targetsSection = - new TargetsSection(ImmutableList.of("//..."), ImmutableList.of()); - - return ProjectView.builder().directories(directoriesSection).targets(targetsSection).build(); - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewProvider.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewProvider.java index 84c277880..f4e6a3171 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewProvider.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewProvider.java @@ -1,6 +1,8 @@ package org.jetbrains.bsp.bazel.projectview.model; +import io.vavr.control.Try; + public interface ProjectViewProvider { - ProjectView create(); + Try create(); } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD index 8ddfbae3d..85b7e34a7 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD @@ -3,5 +3,12 @@ load("@rules_java//java:defs.bzl", "java_library") java_library( name = "sections", srcs = glob(["*.java"]), - visibility = ["//projectview:__subpackages__"], + visibility = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview:__subpackages__", + "//projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections:__pkg__", + ], + deps = [ + "@maven//:com_google_guava_guava", + "@maven//:org_apache_commons_commons_collections4", + ], ) diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewBazelPathSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewBazelPathSection.java new file mode 100644 index 000000000..92767d1d0 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewBazelPathSection.java @@ -0,0 +1,15 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +public class ProjectViewBazelPathSection extends ProjectViewSingletonSection { + + public static final String SECTION_NAME = "bazel_path"; + + public ProjectViewBazelPathSection(String value) { + super(SECTION_NAME, value); + } + + @Override + public String toString() { + return "ProjectViewBazelPathSection{" + "value=" + value + "} "; + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewDebuggerAddressSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewDebuggerAddressSection.java new file mode 100644 index 000000000..eb11414b2 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewDebuggerAddressSection.java @@ -0,0 +1,15 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +public class ProjectViewDebuggerAddressSection extends ProjectViewSingletonSection { + + public static final String SECTION_NAME = "debugger_address"; + + public ProjectViewDebuggerAddressSection(String value) { + super(SECTION_NAME, value); + } + + @Override + public String toString() { + return "ProjectViewDebuggerAddressSection{" + "value=" + value + "} "; + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewJavaPathSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewJavaPathSection.java new file mode 100644 index 000000000..d684b734c --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewJavaPathSection.java @@ -0,0 +1,15 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +public class ProjectViewJavaPathSection extends ProjectViewSingletonSection { + + public static final String SECTION_NAME = "java_path"; + + public ProjectViewJavaPathSection(String value) { + super(SECTION_NAME, value); + } + + @Override + public String toString() { + return "ProjectViewJavaPathSection{" + "value=" + value + "} "; + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSection.java new file mode 100644 index 000000000..0ee468b28 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSection.java @@ -0,0 +1,47 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +import java.util.List; +import java.util.Objects; +import org.apache.commons.collections4.CollectionUtils; + +public abstract class ProjectViewListSection extends ProjectViewSection { + + protected final List includedValues; + protected final List excludedValues; + + protected ProjectViewListSection(String sectionName) { + super(sectionName); + this.includedValues = List.of(); + this.excludedValues = List.of(); + } + + protected ProjectViewListSection( + String sectionName, List includedValues, List excludedValues) { + super(sectionName); + this.includedValues = includedValues; + this.excludedValues = excludedValues; + } + + public List getIncludedValues() { + return includedValues; + } + + public List getExcludedValues() { + return excludedValues; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProjectViewListSection)) return false; + ProjectViewListSection that = (ProjectViewListSection) o; + return CollectionUtils.isEqualCollection(includedValues, that.includedValues) + && CollectionUtils.isEqualCollection(excludedValues, that.excludedValues) + && sectionName.equals(that.sectionName); + } + + @Override + public int hashCode() { + return Objects.hash(includedValues, excludedValues, sectionName); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSection.java index c12782476..56d595dbf 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSection.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSection.java @@ -1,12 +1,14 @@ package org.jetbrains.bsp.bazel.projectview.model.sections; -public abstract class ProjectViewSection> { +abstract class ProjectViewSection { - protected final ProjectViewSectionHeader header; + protected final String sectionName; - protected ProjectViewSection(ProjectViewSectionHeader header) { - this.header = header; + protected ProjectViewSection(String sectionName) { + this.sectionName = sectionName; } - public abstract T merge(T otherSection); + public String getSectionName() { + return sectionName; + } } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSectionHeader.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSectionHeader.java deleted file mode 100644 index 5624cc9fd..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSectionHeader.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.model.sections; - -public enum ProjectViewSectionHeader { - DIRECTORIES("directories"), - TARGETS("targets"); - - private final String name; - - ProjectViewSectionHeader(String value) { - name = value; - } - - public String toString() { - return name; - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSection.java new file mode 100644 index 000000000..0e83ddade --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSection.java @@ -0,0 +1,30 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +import java.util.Objects; + +public abstract class ProjectViewSingletonSection extends ProjectViewSection { + + protected final String value; + + protected ProjectViewSingletonSection(String sectionName, String value) { + super(sectionName); + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProjectViewSingletonSection)) return false; + ProjectViewSingletonSection that = (ProjectViewSingletonSection) o; + return value.equals(that.value) && sectionName.equals(that.sectionName); + } + + @Override + public int hashCode() { + return Objects.hash(value, sectionName); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewTargetsSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewTargetsSection.java new file mode 100644 index 000000000..5870ed798 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewTargetsSection.java @@ -0,0 +1,26 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +import java.util.List; + +public class ProjectViewTargetsSection extends ProjectViewListSection { + + public static final String SECTION_NAME = "targets"; + + public ProjectViewTargetsSection() { + super(SECTION_NAME); + } + + public ProjectViewTargetsSection(List includedValues, List excludedValues) { + super(SECTION_NAME, includedValues, excludedValues); + } + + @Override + public String toString() { + return "ProjectViewTargetsSection{" + + "includedValues=" + + includedValues + + ", excludedValues=" + + excludedValues + + "} "; + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/BUILD deleted file mode 100644 index c096e3871..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/BUILD +++ /dev/null @@ -1,11 +0,0 @@ -load("@rules_java//java:defs.bzl", "java_library") - -java_library( - name = "specific", - srcs = glob(["*.java"]), - visibility = ["//projectview:__subpackages__"], - deps = [ - "//commons", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", - ], -) diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/DirectoriesSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/DirectoriesSection.java deleted file mode 100644 index 32a7bfc2c..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/DirectoriesSection.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.model.sections.specific; - -import java.nio.file.Path; -import java.util.List; -import java.util.Objects; -import org.jetbrains.bsp.bazel.commons.ListUtils; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; - -public class DirectoriesSection extends ProjectViewSection { - - private final List includedDirectories; - private final List excludedDirectories; - - public DirectoriesSection(List includedDirectories, List excludedDirectories) { - super(ProjectViewSectionHeader.DIRECTORIES); - this.includedDirectories = includedDirectories; - this.excludedDirectories = excludedDirectories; - } - - @Override - public DirectoriesSection merge(DirectoriesSection otherSection) { - List mergedIncludedDirectories = - ListUtils.concat(includedDirectories, otherSection.includedDirectories); - List mergedExcludedDirectories = - ListUtils.concat(excludedDirectories, otherSection.excludedDirectories); - - return new DirectoriesSection(mergedIncludedDirectories, mergedExcludedDirectories); - } - - public List getIncludedDirectories() { - return includedDirectories; - } - - public List getExcludedDirectories() { - return excludedDirectories; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DirectoriesSection that = (DirectoriesSection) o; - return Objects.equals(includedDirectories, that.includedDirectories) - && Objects.equals(excludedDirectories, that.excludedDirectories); - } - - @Override - public int hashCode() { - return Objects.hash(includedDirectories, excludedDirectories); - } - - @Override - public String toString() { - return "DirectoriesSection{" - + "includedDirectories=" - + includedDirectories - + ", excludedDirectories=" - + excludedDirectories - + '}'; - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/TargetsSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/TargetsSection.java deleted file mode 100644 index 892e34515..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections/specific/TargetsSection.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.model.sections.specific; - -import java.util.List; -import java.util.Objects; -import org.jetbrains.bsp.bazel.commons.ListUtils; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; - -public class TargetsSection extends ProjectViewSection { - - private final List includedTargets; - private final List excludedTargets; - - public TargetsSection(List includedTargets, List excludedTargets) { - super(ProjectViewSectionHeader.TARGETS); - this.includedTargets = includedTargets; - this.excludedTargets = excludedTargets; - } - - @Override - public TargetsSection merge(TargetsSection otherSection) { - List mergedIncludedTargets = - ListUtils.concat(includedTargets, otherSection.includedTargets); - List mergedExcludedTargets = - ListUtils.concat(excludedTargets, otherSection.excludedTargets); - - return new TargetsSection(mergedIncludedTargets, mergedExcludedTargets); - } - - public List getIncludedTargets() { - return includedTargets; - } - - public List getExcludedTargets() { - return excludedTargets; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TargetsSection that = (TargetsSection) o; - return Objects.equals(includedTargets, that.includedTargets) - && Objects.equals(excludedTargets, that.excludedTargets); - } - - @Override - public int hashCode() { - return Objects.hash(includedTargets, excludedTargets); - } - - @Override - public String toString() { - return "TargetsSection{" - + "includedTargets=" - + includedTargets - + ", excludedTargets=" - + excludedTargets - + '}'; - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD index d1db834a9..cceabc329 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD @@ -4,12 +4,14 @@ java_library( name = "parser", srcs = glob(["*.java"]), visibility = ["//projectview:__subpackages__"], + exports = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model", + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections", + ], deps = [ "//commons", - "//projectview:model", + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter", "@maven//:com_google_guava_guava", "@maven//:io_vavr_vavr", "@maven//:org_apache_logging_log4j_log4j_api", diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewDefaultParserProvider.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewDefaultParserProvider.java index 8d051990e..6b3b2741f 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewDefaultParserProvider.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewDefaultParserProvider.java @@ -2,27 +2,25 @@ import io.vavr.control.Try; import java.nio.file.Path; -import java.nio.file.Paths; import org.jetbrains.bsp.bazel.commons.Constants; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; -import org.jetbrains.bsp.bazel.projectview.model.ProjectViewDefaultProvider; import org.jetbrains.bsp.bazel.projectview.model.ProjectViewProvider; +@Deprecated public class ProjectViewDefaultParserProvider implements ProjectViewProvider { private static final ProjectViewParser PARSER = new ProjectViewParserImpl(); - private static final Path PROJECT_VIEW_FILE = Paths.get(Constants.DEFAULT_PROJECT_VIEW_FILE); - private static final ProjectViewProvider PROJECT_VIEW_PROVIDER = new ProjectViewDefaultProvider(); - @Override - public ProjectView create() { - return Try.success(PROJECT_VIEW_FILE) - .filter(this::doesProjectViewFileExists) - .mapTry(PARSER::parse) - .getOrElse(PROJECT_VIEW_PROVIDER::create); + private final Path projectViewFile; + private final Path defaultProjectViewFile; + + public ProjectViewDefaultParserProvider(Path bspProjectRoot) { + this.projectViewFile = bspProjectRoot.resolve(Constants.PROJECT_VIEW_FILE_PATH); + this.defaultProjectViewFile = bspProjectRoot.resolve(Constants.DEFAULT_PROJECT_VIEW_FILE_PATH); } - private boolean doesProjectViewFileExists(Path projectViewFile) { - return projectViewFile.toFile().isFile(); + @Override + public Try create() { + return PARSER.parse(projectViewFile, defaultProjectViewFile); } } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewImportParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewImportParser.java deleted file mode 100644 index eea665e56..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewImportParser.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser; - -import io.vavr.control.Try; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.bsp.bazel.projectview.model.ProjectView; -import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; - -public class ProjectViewImportParser { - - private static final Logger LOGGER = LogManager.getLogger(ProjectViewImportParser.class); - - private static final String IMPORT_SECTION_HEADER = "import"; - - private final ProjectViewParser projectViewParser; - - public ProjectViewImportParser(ProjectViewParser projectViewParser) { - this.projectViewParser = projectViewParser; - } - - public Optional parseRawSections(List rawSections) { - return rawSections.stream() - .filter(this::isImportSection) - .findFirst() - .map(this::getProjectViewFile) - .flatMap(this::parseFile); - } - - private boolean isImportSection(ProjectViewRawSection rawSection) { - return rawSection.getSectionHeader().equals(IMPORT_SECTION_HEADER); - } - - private Path getProjectViewFile(ProjectViewRawSection rawSection) { - String projectViewFilePath = rawSection.getSectionBody().trim(); - - return Paths.get(projectViewFilePath); - } - - private Optional parseFile(Path projectViewPath) { - return Try.success(projectViewPath) - .mapTry(projectViewParser::parse) - .onFailure(LOGGER::error) - .toJavaOptional(); - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParser.java index d150d0568..2866910f4 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParser.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParser.java @@ -1,25 +1,109 @@ package org.jetbrains.bsp.bazel.projectview.parser; -import com.google.common.io.Files; -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; +import io.vavr.control.Try; import java.nio.file.Path; +import org.jetbrains.bsp.bazel.commons.BetterFiles; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; /** - * Project view file parser. Its purpose is to parse *.bazelproject file and create ProjectView + * Project view file parser. Its purpose is to parse *.bazelproject file and create an instance of + * ProjectView. * * @see org.jetbrains.bsp.bazel.projectview.model.ProjectView */ public interface ProjectViewParser { - ProjectView parse(String projectViewFileContent); + /** + * Parses file under projectViewFilePath using file under + * defaultProjectViewFilePath as a default. + * + * @param projectViewFilePath path to file with project view + * @param defaultProjectViewFilePath path to file with default project view + * @return + *

Try.success with ProjectView if parsing has finished with + * success, it means: + *

1) files under projectViewFilePath and defaultProjectViewFilePath + * were successfully parsed (not all values have to be provided -- some fields in + * ProjectView might be Optional.empty).
+ * File under projectViewFilePath can contain all values, then + * defaultProjectViewFilePath won't be used, or file under projectViewFilePath + * can be empty, then all values from file under defaultProjectViewFilePath + * will be used, any other configuration is possible as well. + *

2) file under projectViewFilePath doesn't exist, then all values from + * defaultProjectViewFilePath will be used.
+ *
+ *

Try.failure with if: + *

1) file under defaultProjectViewFilePath doesn't exist + *

2) any other fail happen + */ + default Try parse(Path projectViewFilePath, Path defaultProjectViewFilePath) { + return BetterFiles.tryReadFileContent(defaultProjectViewFilePath) + .flatMap( + defaultProjectViewFileContent -> + parseWithDefault(projectViewFilePath, defaultProjectViewFileContent)); + } + + private Try parseWithDefault( + Path projectViewFilePath, String defaultProjectViewFileContent) { + return BetterFiles.tryReadFileContent(projectViewFilePath) + .flatMap( + projectViewFilePathContent -> + parse(projectViewFilePathContent, defaultProjectViewFileContent)) + .orElse(parse(defaultProjectViewFileContent)); + } - default ProjectView parse(Path projectViewPath) throws IOException { - File projectViewFile = projectViewPath.toFile(); - String fileContent = Files.asCharSource(projectViewFile, Charset.defaultCharset()).read(); + /** + * Parses projectViewFileContent using defaultProjectViewFileContent as + * a default. + * + * @param projectViewFileContent string with project view + * @param defaultProjectViewFileContent string with default project view + * @return + *

Try.success with ProjectView if parsing has finished with + * success, it means: + *

1) projectViewFileContent and defaultProjectViewFileContent + * were successfully parsed (not all values have to be provided -- some fields in + * ProjectView might be Optional.empty).
+ * projectViewFileContent can contain all values, then + * defaultProjectViewFileContent won't be used, projectViewFileContent + * can be empty, then all values from defaultProjectViewFileContent will + * be used, any other configuration is possible as well.
+ *
+ *

Try.failure with if: + *

1) any fail happen + */ + Try parse(String projectViewFileContent, String defaultProjectViewFileContent); - return parse(fileContent); + /** + * Parses file under projectViewFilePath. + * + * @param projectViewFilePath path to file with project view + * @return + *

Try.success with ProjectView if parsing has finished with + * success, it means: + *

1) file under projectViewFilePath was successfully parsed (not all values + * have to be provided -- some fields in ProjectView might be + * Optional.empty).
+ *

Try.failure with if: + *

1) file under projectViewFilePath doesn't exist + *

2) any other fail happen + */ + default Try parse(Path projectViewFilePath) { + return BetterFiles.tryReadFileContent(projectViewFilePath).flatMap(this::parse); } + + /** + * Parses projectViewFileContent. + * + * @param projectViewFileContent string with project view + * @return + *

Try.success with ProjectView if parsing has finished with + * success, it means: + *

1) projectViewFileContent was successfully parsed (not all values have to + * be provided -- some fields in ProjectView might be Optional.empty + * ).
+ *

Try.failure with if: + *

1) any fail happen + */ + Try parse(String projectViewFileContent); } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImpl.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImpl.java index fc7ad458f..8acd7d037 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImpl.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImpl.java @@ -1,69 +1,85 @@ package org.jetbrains.bsp.bazel.projectview.parser; +import io.vavr.control.Try; +import java.nio.file.Paths; import java.util.List; -import java.util.Optional; +import java.util.stream.Collectors; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.specific.DirectoriesSectionParser; -import org.jetbrains.bsp.bazel.projectview.parser.sections.specific.TargetsSectionParser; +import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewBazelPathSectionParser; +import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewDebuggerAddressSectionParser; +import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewJavaPathSectionParser; +import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewTargetsSectionParser; import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewSectionSplitter; /** - * Our default implementation of ProjectViewParser - * - *

Logic schema: - * - *

1. extracting blocks:

: - * - - - * - *

or:

:
- * - *

2. looping through extracted and checking which block could be parsed by the given section - * parser - * - *

3. applying section specific parser to the chosen section + * Default implementation of ProjectViewParser. * * @see org.jetbrains.bsp.bazel.projectview.parser.ProjectViewParser * @see org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewSectionSplitter - * @see org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewSectionParser */ class ProjectViewParserImpl implements ProjectViewParser { - private static final ProjectViewRawSectionParser DIRECTORY_PARSER = - ProjectViewRawSectionParser.forParser(new DirectoriesSectionParser()); + private static final String IMPORT_STATEMENT = "import"; - private static final ProjectViewRawSectionParser TARGETS_PARSER = - ProjectViewRawSectionParser.forParser(new TargetsSectionParser()); + private static final ProjectViewTargetsSectionParser targetsParser = + new ProjectViewTargetsSectionParser(); - private final ProjectViewImportParser projectViewImportParser; + private static final ProjectViewBazelPathSectionParser bazelPathParser = + new ProjectViewBazelPathSectionParser(); - public ProjectViewParserImpl() { - this.projectViewImportParser = new ProjectViewImportParser(this); - } + private static final ProjectViewDebuggerAddressSectionParser debuggerAddressParser = + new ProjectViewDebuggerAddressSectionParser(); + + private static final ProjectViewJavaPathSectionParser javaPathParser = + new ProjectViewJavaPathSectionParser(); @Override - public ProjectView parse(String projectViewFileContent) { - List rawSections = - ProjectViewSectionSplitter.split(projectViewFileContent); + public Try parse( + String projectViewFileContent, String defaultProjectViewFileContent) { + return parse(defaultProjectViewFileContent) + .flatMap( + defaultProjectView -> parseWithDefault(projectViewFileContent, defaultProjectView)); + } - ProjectView projectView = buildFile(rawSections); - Optional importedProjectView = - projectViewImportParser.parseRawSections(rawSections); + private Try parseWithDefault( + String projectViewFileContent, ProjectView defaultProjectView) { + ProjectViewRawSections rawSections = + ProjectViewSectionSplitter.getRawSectionsForFileContent(projectViewFileContent); - return mergeProjectViewIfNeeded(projectView, importedProjectView); + return ProjectView.builder() + .imports(findImportedProjectViews(rawSections)) + .targets(targetsParser.parseOrDefault(rawSections, defaultProjectView.getTargets())) + .bazelPath(bazelPathParser.parseOrDefault(rawSections, defaultProjectView.getBazelPath())) + .debuggerAddress( + debuggerAddressParser.parseOrDefault( + rawSections, defaultProjectView.getDebuggerAddress())) + .javaPath(javaPathParser.parseOrDefault(rawSections, defaultProjectView.getJavaPath())) + .build(); } - private ProjectView buildFile(List rawSections) { + @Override + public Try parse(String projectViewFileContent) { + ProjectViewRawSections rawSections = + ProjectViewSectionSplitter.getRawSectionsForFileContent(projectViewFileContent); + return ProjectView.builder() - .directories(DIRECTORY_PARSER.parseRawSections(rawSections)) - .targets(TARGETS_PARSER.parseRawSections(rawSections)) + .imports(findImportedProjectViews(rawSections)) + .targets(targetsParser.parse(rawSections)) + .bazelPath(bazelPathParser.parse(rawSections)) + .debuggerAddress(debuggerAddressParser.parse(rawSections)) + .javaPath(javaPathParser.parse(rawSections)) .build(); } - private ProjectView mergeProjectViewIfNeeded( - ProjectView projectView, Optional importedProjectView) { - return importedProjectView.map(imported -> imported.merge(projectView)).orElse(projectView); + private List> findImportedProjectViews(ProjectViewRawSections rawSections) { + return rawSections + .getAllWithName(IMPORT_STATEMENT) + .map(ProjectViewRawSection::getSectionBody) + .map(String::trim) + .map(Paths::get) + .map(this::parse) + .collect(Collectors.toList()); } } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewRawSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewRawSectionParser.java deleted file mode 100644 index baa7124b6..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewRawSectionParser.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser; - -import java.util.List; -import java.util.Optional; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewSectionParser; -import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; - -public class ProjectViewRawSectionParser { - - private final ProjectViewSectionParser parser; - - private ProjectViewRawSectionParser(ProjectViewSectionParser parser) { - this.parser = parser; - } - - public static ProjectViewRawSectionParser forParser( - ProjectViewSectionParser parser) { - return new ProjectViewRawSectionParser<>(parser); - } - - public T parseRawSections(List rawSections) { - return parseOptionalRawSections(rawSections) - .orElseThrow( - () -> - new IllegalArgumentException(this.parser.sectionName() + " section is required!")); - } - - public Optional parseOptionalRawSections(List rawSections) { - return rawSections.stream() - .filter(this::isSectionParsable) - .findFirst() - .map(section -> parser.parse(section.getSectionBody())); - } - - private boolean isSectionParsable(ProjectViewRawSection rawSection) { - return parser.isSectionParsable(rawSection.getSectionHeader()); - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD index 541d48b7d..b32ea5ce6 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD @@ -3,10 +3,21 @@ load("@rules_java//java:defs.bzl", "java_library") java_library( name = "sections", srcs = glob(["*.java"]), - visibility = ["//projectview:__subpackages__"], + visibility = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser:__subpackages__", + "//projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections:__pkg__", + ], + exports = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter", + "@maven//:io_vavr_vavr", + ], deps = [ - "//projectview:model", + "//commons", + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter", "@maven//:com_google_guava_guava", + "@maven//:io_vavr_vavr", "@maven//:org_apache_logging_log4j_log4j_api", "@maven//:org_apache_logging_log4j_log4j_core", ], diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewBazelPathSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewBazelPathSectionParser.java new file mode 100644 index 000000000..e85904508 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewBazelPathSectionParser.java @@ -0,0 +1,16 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; + +public class ProjectViewBazelPathSectionParser + extends ProjectViewSingletonSectionParser { + + public ProjectViewBazelPathSectionParser() { + super(ProjectViewBazelPathSection.SECTION_NAME); + } + + @Override + protected ProjectViewBazelPathSection createInstance(String value) { + return new ProjectViewBazelPathSection(value); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewDebuggerAddressSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewDebuggerAddressSectionParser.java new file mode 100644 index 000000000..2e8d89838 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewDebuggerAddressSectionParser.java @@ -0,0 +1,16 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDebuggerAddressSection; + +public class ProjectViewDebuggerAddressSectionParser + extends ProjectViewSingletonSectionParser { + + public ProjectViewDebuggerAddressSectionParser() { + super(ProjectViewDebuggerAddressSection.SECTION_NAME); + } + + @Override + protected ProjectViewDebuggerAddressSection createInstance(String value) { + return new ProjectViewDebuggerAddressSection(value); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewJavaPathSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewJavaPathSectionParser.java new file mode 100644 index 000000000..0c128bc51 --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewJavaPathSectionParser.java @@ -0,0 +1,16 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewJavaPathSection; + +public class ProjectViewJavaPathSectionParser + extends ProjectViewSingletonSectionParser { + + public ProjectViewJavaPathSectionParser() { + super(ProjectViewJavaPathSection.SECTION_NAME); + } + + @Override + protected ProjectViewJavaPathSection createInstance(String value) { + return new ProjectViewJavaPathSection(value); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewListSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewListSectionParser.java index b757132c6..a8cd3910b 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewListSectionParser.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewListSectionParser.java @@ -1,57 +1,91 @@ package org.jetbrains.bsp.bazel.projectview.parser.sections; +import com.google.common.base.Splitter; +import io.vavr.control.Try; import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; +import org.jetbrains.bsp.bazel.commons.ListUtils; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewListSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; -public abstract class ProjectViewListSectionParser +/** + * Implementation of list section parser. + * + *

It takes a raw section and search for entries - they are split by whitespaces, if entry starts + * with '-' -- this entry is excluded, otherwise included. + * + * @param type of parsed list section + */ +abstract class ProjectViewListSectionParser extends ProjectViewSectionParser { private static final String EXCLUDED_ENTRY_PREFIX = "-"; + private static final Pattern WHITESPACE_CHAR_REGEX = Pattern.compile("[ \n\t]+"); - private final boolean exclusionary; + protected ProjectViewListSectionParser(String sectionName) { + super(sectionName); + } - protected ProjectViewListSectionParser( - ProjectViewSectionHeader sectionHeader, boolean exclusionary) { - super(sectionHeader); - this.exclusionary = exclusionary; + @Override + public T parse(ProjectViewRawSections rawSections) { + return parseAllSectionsAndMerge(rawSections).orElse(createInstance(List.of(), List.of())); } - protected List parseIncludedEntries(String sectionBody) { - List entries = splitSectionEntries(sectionBody); - List includedEntries = getIncludedEntries(entries); + @Override + public T parseOrDefault(ProjectViewRawSections rawSections, T defaultValue) { + return parseAllSectionsAndMerge(rawSections).orElse(defaultValue); + } - return getValueIfExclusionaryOrElse(includedEntries, entries); + private Optional parseAllSectionsAndMerge(ProjectViewRawSections rawSections) { + return rawSections + .getAllWithName(sectionName) + .map(this::parse) + .map(Try::get) + .reduce(this::concatSectionsItems); } - private List getIncludedEntries(List entries) { - return entries.stream().filter(entry -> !isExcluded(entry)).collect(Collectors.toList()); + private T concatSectionsItems(T section1, T section2) { + var includedItems = + ListUtils.concat(section1.getIncludedValues(), section2.getIncludedValues()); + var excludedItems = + ListUtils.concat(section1.getExcludedValues(), section2.getExcludedValues()); + + return createInstance(includedItems, excludedItems); + } + + @Override + protected T parse(String sectionBody) { + var allEntries = splitSectionEntries(sectionBody); + var includedEntries = filterIncludedEntries(allEntries); + var excludedEntries = filterExcludedEntries(allEntries); + + return createInstance(includedEntries, excludedEntries); } - protected List parseExcludedEntries(String sectionBody) { - List entries = splitSectionEntries(sectionBody); - List excludedEntries = getExcludedEntries(entries); + private List splitSectionEntries(String sectionBody) { + return Splitter.on(WHITESPACE_CHAR_REGEX).omitEmptyStrings().splitToList(sectionBody); + } - return getValueIfExclusionaryOrElse(excludedEntries, entries); + private List filterIncludedEntries(List entries) { + return entries.stream().filter(entry -> !isExcluded(entry)).collect(Collectors.toList()); } - private List getExcludedEntries(List entries) { + private List filterExcludedEntries(List entries) { return entries.stream() .filter(this::isExcluded) - .map(entry -> entry.substring(1)) + .map(this::removeExcludedEntryPrefix) .collect(Collectors.toList()); } - private List getValueIfExclusionaryOrElse(List value, List elseValue) { - if (exclusionary) { - return value; - } else { - return elseValue; - } + private String removeExcludedEntryPrefix(String excludedEntry) { + return excludedEntry.substring(1); } private boolean isExcluded(String entry) { return entry.startsWith(EXCLUDED_ENTRY_PREFIX); } + + protected abstract T createInstance(List includedValues, List excludedValues); } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSectionParser.java index 0907ae44c..bf9a301c7 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSectionParser.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSectionParser.java @@ -1,36 +1,38 @@ package org.jetbrains.bsp.bazel.projectview.parser.sections; -import com.google.common.base.Splitter; -import java.util.List; -import java.util.regex.Pattern; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; - -/** - * Project view file section parser, implementation should parse raw string body into given section - * class - * - * @param project view section which can be parsed with implementation - */ -public abstract class ProjectViewSectionParser { - - private final ProjectViewSectionHeader sectionHeader; - - protected ProjectViewSectionParser(ProjectViewSectionHeader sectionHeader) { - this.sectionHeader = sectionHeader; - } +import io.vavr.control.Try; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; + +abstract class ProjectViewSectionParser { - public abstract T parse(String sectionBody); + protected final String sectionName; - public boolean isSectionParsable(String sectionHeader) { - return sectionHeader.equals(this.sectionHeader.toString()); + protected ProjectViewSectionParser(String sectionName) { + this.sectionName = sectionName; } - public String sectionName() { - return sectionHeader.toString(); + public abstract T parse(ProjectViewRawSections rawSections); + + public abstract T parseOrDefault(ProjectViewRawSections rawSections, T defaultValue); + + public Try parse(ProjectViewRawSection rawSection) { + return getSectionBodyOrFailureIfNameIsWrong(rawSection).map(this::parse); } - protected List splitSectionEntries(String sectionBody) { - return Splitter.on(Pattern.compile("[ \n\t]+")).omitEmptyStrings().splitToList(sectionBody); + private Try getSectionBodyOrFailureIfNameIsWrong(ProjectViewRawSection rawSection) { + if (!rawSection.hasName(sectionName)) { + var exceptionMessage = + "Project view parsing failed! Expected '" + + sectionName + + "' section name, got '" + + rawSection.getSectionName() + + "'!"; + return Try.failure(new IllegalArgumentException(exceptionMessage)); + } + + return Try.success(rawSection.getSectionBody()); } + + protected abstract T parse(String sectionBody); } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParser.java new file mode 100644 index 000000000..cfc02dafa --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParser.java @@ -0,0 +1,40 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import io.vavr.control.Try; +import java.util.Optional; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSingletonSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; + +/** + * Implementation of single value section parser. + * + *

It takes a raw section and trims the content. + * + * @param type of parsed single value section + */ +abstract class ProjectViewSingletonSectionParser + extends ProjectViewSectionParser> { + + protected ProjectViewSingletonSectionParser(String sectionName) { + super(sectionName); + } + + @Override + public Optional parseOrDefault(ProjectViewRawSections rawSections, Optional defaultValue) { + return parse(rawSections).or(() -> defaultValue); + } + + @Override + public Optional parse(ProjectViewRawSections rawSections) { + return rawSections.getLastSectionWithName(sectionName).map(this::parse).flatMap(Try::get); + } + + @Override + protected Optional parse(String sectionBody) { + return Optional.of(sectionBody.strip()) + .filter(body -> !body.isEmpty()) + .map(this::createInstance); + } + + protected abstract T createInstance(String value); +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParser.java new file mode 100644 index 000000000..0913435fe --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParser.java @@ -0,0 +1,18 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import java.util.List; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; + +public class ProjectViewTargetsSectionParser + extends ProjectViewListSectionParser { + + public ProjectViewTargetsSectionParser() { + super(ProjectViewTargetsSection.SECTION_NAME); + } + + @Override + protected ProjectViewTargetsSection createInstance( + List includedValues, List excludedValues) { + return new ProjectViewTargetsSection(includedValues, excludedValues); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD deleted file mode 100644 index 3372d3f22..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD +++ /dev/null @@ -1,12 +0,0 @@ -load("@rules_java//java:defs.bzl", "java_library") - -java_library( - name = "specific", - srcs = glob(["*.java"]), - visibility = ["//projectview:__subpackages__"], - deps = [ - "//projectview:model", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections", - "@maven//:com_google_guava_guava", - ], -) diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParser.java deleted file mode 100644 index 31a5e80ed..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParser.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser.sections.specific; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewListSectionParser; - -public class DirectoriesSectionParser extends ProjectViewListSectionParser { - - public DirectoriesSectionParser() { - super(ProjectViewSectionHeader.DIRECTORIES, true); - } - - @Override - public DirectoriesSection parse(String sectionBody) { - List includedEntries = parseIncludedEntries(sectionBody); - List includedPaths = mapEntriesToPaths(includedEntries); - - List excludedEntries = parseExcludedEntries(sectionBody); - List excludedPaths = mapEntriesToPaths(excludedEntries); - - return new DirectoriesSection(includedPaths, excludedPaths); - } - - private List mapEntriesToPaths(List entries) { - return entries.stream().map(Paths::get).collect(Collectors.toList()); - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParser.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParser.java deleted file mode 100644 index 42aeda1ef..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParser.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser.sections.specific; - -import java.util.List; -import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSectionHeader; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewListSectionParser; - -public class TargetsSectionParser extends ProjectViewListSectionParser { - - public TargetsSectionParser() { - super(ProjectViewSectionHeader.TARGETS, true); - } - - @Override - public TargetsSection parse(String sectionBody) { - List includedEntries = parseIncludedEntries(sectionBody); - List excludedEntries = parseExcludedEntries(sectionBody); - - return new TargetsSection(includedEntries, excludedEntries); - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD index b2c5923e8..15b472c2e 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD @@ -3,8 +3,12 @@ load("@rules_java//java:defs.bzl", "java_library") java_library( name = "splitter", srcs = glob(["*.java"]), - visibility = ["//projectview:__subpackages__"], + visibility = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser:__subpackages__", + "//projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter:__pkg__", + ], deps = [ "@maven//:com_google_guava_guava", + "@maven//:org_apache_commons_commons_collections4", ], ) diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSection.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSection.java index 3213df615..96b993d14 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSection.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSection.java @@ -4,16 +4,20 @@ public class ProjectViewRawSection { - private final String sectionHeader; + private final String sectionName; private final String sectionBody; - public ProjectViewRawSection(String sectionHeader, String sectionBody) { - this.sectionHeader = sectionHeader; + public ProjectViewRawSection(String sectionName, String sectionBody) { + this.sectionName = sectionName; this.sectionBody = sectionBody; } - public String getSectionHeader() { - return sectionHeader; + public boolean hasName(String thatName) { + return sectionName.equals(thatName); + } + + public String getSectionName() { + return sectionName; } public String getSectionBody() { @@ -23,8 +27,8 @@ public String getSectionBody() { @Override public String toString() { return "ProjectViewRawSection{" - + "sectionHeader='" - + sectionHeader + + "sectionName='" + + sectionName + '\'' + ", sectionBody='" + sectionBody @@ -34,19 +38,14 @@ public String toString() { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) return true; + if (!(o instanceof ProjectViewRawSection)) return false; ProjectViewRawSection that = (ProjectViewRawSection) o; - return Objects.equals(sectionHeader, that.sectionHeader) - && Objects.equals(sectionBody, that.sectionBody); + return sectionName.equals(that.sectionName) && sectionBody.equals(that.sectionBody); } @Override public int hashCode() { - return Objects.hash(sectionHeader, sectionBody); + return Objects.hash(sectionName, sectionBody); } } diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSections.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSections.java new file mode 100644 index 000000000..765f451ef --- /dev/null +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSections.java @@ -0,0 +1,42 @@ +package org.jetbrains.bsp.bazel.projectview.parser.splitter; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.commons.collections4.CollectionUtils; + +public class ProjectViewRawSections { + + private final List sections; + + public ProjectViewRawSections(List sections) { + this.sections = sections; + } + + public Optional getLastSectionWithName(String sectionName) { + return getAllWithName(sectionName).reduce((first, second) -> second); + } + + public Stream getAllWithName(String sectionName) { + return sections.stream().filter(section -> section.hasName(sectionName)); + } + + @Override + public String toString() { + return "ProjectViewRawSections{" + "sections=" + sections + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ProjectViewRawSections)) return false; + ProjectViewRawSections sections1 = (ProjectViewRawSections) o; + return CollectionUtils.isEqualCollection(sections, sections1.sections); + } + + @Override + public int hashCode() { + return Objects.hash(sections); + } +} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionHeaderPosition.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionHeaderPosition.java deleted file mode 100644 index 1a912cc85..000000000 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionHeaderPosition.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser.splitter; - -class ProjectViewSectionHeaderPosition { - - private final int startIndex; - private final int endIndex; - - private final String header; - - ProjectViewSectionHeaderPosition(int startIndex, int endIndex, String header) { - this.startIndex = startIndex; - this.endIndex = endIndex; - this.header = header; - } - - int getStartIndex() { - return startIndex; - } - - int getEndIndex() { - return endIndex; - } - - String getHeader() { - return header; - } -} diff --git a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitter.java b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitter.java index 9681b5cd6..4aed6bfdf 100644 --- a/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitter.java +++ b/projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitter.java @@ -1,91 +1,78 @@ package org.jetbrains.bsp.bazel.projectview.parser.splitter; +import com.google.common.collect.Streams; import java.util.ArrayList; import java.util.List; -import java.util.ListIterator; -import java.util.Optional; -import java.util.regex.Matcher; import java.util.regex.Pattern; - +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Splitter is responsible for splitting file content into "raw" sections - section name and entire + * section body. Also, it removes comments (starting with '#') from section content + * + *

e.g.:
+ * file content: + * + *


+ * ---
+ *    import path/to/another/file.bazelproject
+ *
+ *    section1: value1 # comment
+ *
+ *    section2:
+ *      included_value1
+ *      included_value2
+ *      -excluded_value3
+ * ---
+ * 
+ * + *

will be split into raw sections:
+ * 1) 'import' -- 'path/to/another/file.bazelproject\n\n'
+ * 2) 'section1' -- 'value1 \n\n'
+ * 3) 'section2' -- + * '\n included_value1\n included_value2\n -excluded_value3\n'
+ */ public final class ProjectViewSectionSplitter { - private static final String SECTION_HEADER_REGEX = "(^[a-z_]+[:]?)"; - private static final String POSSIBLE_HEADER_ENDING_CHARACTER = ":"; - - public static List split(String fileContent) { - List sectionsHeadersPositions = - splitIntoSectionsHeadersPositions(fileContent); + private static final Pattern SECTION_HEADER_REGEX = + Pattern.compile("((^[^:\\-/*\\s]+)([: ]))", Pattern.MULTILINE); + private static final int SECTION_HEADER_NAME_GROUP_ID = 2; - return parseRawSections(fileContent, sectionsHeadersPositions); - } + private static final String COMMENT_LINE_REGEX = "#(.)*(\\n|\\z)"; + private static final String COMMENT_LINE_REPLACEMENT = "\n"; - private static List splitIntoSectionsHeadersPositions( - String fileContent) { - Pattern pattern = Pattern.compile(SECTION_HEADER_REGEX, Pattern.MULTILINE); - Matcher matcher = pattern.matcher(fileContent); + public static ProjectViewRawSections getRawSectionsForFileContent(String fileContent) { + var fileContentWithoutComments = removeLinesWithComments(fileContent); + var rawSections = findRawSections(fileContentWithoutComments); - return getStartingAndEndingIndexes(matcher); + return new ProjectViewRawSections(rawSections); } - private static List getStartingAndEndingIndexes( - Matcher matcher) { - ArrayList result = new ArrayList<>(); - - while (matcher.find()) { - ProjectViewSectionHeaderPosition position = - new ProjectViewSectionHeaderPosition(matcher.start(), matcher.end(), matcher.group()); + private static String removeLinesWithComments(String fileContent) { + return fileContent.replaceAll(COMMENT_LINE_REGEX, COMMENT_LINE_REPLACEMENT); + } - result.add(position); - } + private static List findRawSections(String fileContent) { + var sectionHeadersNames = findSectionsHeadersNames(fileContent); + var sectionBodies = findSectionsBodiesAndSkipFirstEmptyEntry(fileContent); - return result; + return Streams.zip(sectionHeadersNames.stream(), sectionBodies, ProjectViewRawSection::new) + .collect(Collectors.toList()); } - private static List parseRawSections( - String fileContent, List sectionsHeadersPositions) { - ListIterator iterator = - sectionsHeadersPositions.listIterator(); - List result = new ArrayList<>(); - - while (iterator.hasNext()) { - ProjectViewSectionHeaderPosition currentPosition = iterator.next(); - Optional nextPosition = getNextPosition(iterator); + private static List findSectionsHeadersNames(String fileContent) { + var matcher = SECTION_HEADER_REGEX.matcher(fileContent); + var result = new ArrayList(); - ProjectViewRawSection rawSection = - parseProjectViewRawSection(currentPosition, nextPosition, fileContent); - result.add(rawSection); + while (matcher.find()) { + result.add(matcher.group(SECTION_HEADER_NAME_GROUP_ID)); } return result; } - private static Optional getNextPosition( - ListIterator iterator) { - if (iterator.hasNext()) { - ProjectViewSectionHeaderPosition nextPosition = iterator.next(); - iterator.previous(); - return Optional.of(nextPosition); - } - - return Optional.empty(); - } - - private static ProjectViewRawSection parseProjectViewRawSection( - ProjectViewSectionHeaderPosition currentPosition, - Optional nextPosition, - String fileContent) { - - String sectionBody = - nextPosition - .map(ProjectViewSectionHeaderPosition::getStartIndex) - .map(startIndex -> fileContent.substring(currentPosition.getEndIndex(), startIndex)) - .orElse(fileContent.substring(currentPosition.getEndIndex())); - String sectionHeader = removeEndingHeaderColonAndSpaces(currentPosition.getHeader()); - - return new ProjectViewRawSection(sectionHeader, sectionBody); - } - - private static String removeEndingHeaderColonAndSpaces(String header) { - return header.replace(POSSIBLE_HEADER_ENDING_CHARACTER, "").trim(); + private static Stream findSectionsBodiesAndSkipFirstEmptyEntry(String fileContent) { + return SECTION_HEADER_REGEX.splitAsStream(fileContent).skip(1); } } diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/BUILD b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/BUILD new file mode 100644 index 000000000..4c90beec3 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/BUILD @@ -0,0 +1,13 @@ +load("@rules_java//java:defs.bzl", "java_test") + +java_test( + name = "ProjectViewTest", + size = "small", + srcs = ["ProjectViewTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model", + ], +) diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewTest.java new file mode 100644 index 000000000..fedb08996 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/ProjectViewTest.java @@ -0,0 +1,512 @@ +package org.jetbrains.bsp.bazel.projectview.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import io.vavr.control.Try; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDebuggerAddressSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewJavaPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; +import org.junit.Test; + +public class ProjectViewTest { + + private static final ProjectViewTargetsSection dummyTargetsSection = + new ProjectViewTargetsSection(List.of("//dummy_included_target"), List.of()); + private final Optional dummyBazelPathSection = Optional.empty(); + private final Optional dummyDebuggerAddress = Optional.empty(); + private final Optional dummyJavaPath = Optional.empty(); + + // imports specific tests + + @Test + public void shouldReturnFailureWithFirstCauseForBuilderWithFailureImports() { + // given + var importedProjectViewTry1 = + ProjectView.builder() + .targets(dummyTargetsSection) + .bazelPath(dummyBazelPathSection) + .debuggerAddress(dummyDebuggerAddress) + .javaPath(dummyJavaPath) + .build(); + + var importedProjectViewTry2 = + Try.failure( + new IOException("doesnt/exist/projectview2.bazelproject file does not exist!")); + + var importedProjectViewTry3 = + ProjectView.builder() + .imports(List.of()) + .targets(dummyTargetsSection) + .bazelPath(dummyBazelPathSection) + .debuggerAddress(dummyDebuggerAddress) + .javaPath(dummyJavaPath) + .build(); + + var importedProjectViewTry4 = + Try.failure( + new IOException("doesnt/exist/projectview4.bazelproject file does not exist!")); + + // when + var projectViewTry = + ProjectView.builder() + .imports( + List.of( + importedProjectViewTry1, + importedProjectViewTry2, + importedProjectViewTry3, + importedProjectViewTry4)) + .targets(dummyTargetsSection) + .bazelPath(dummyBazelPathSection) + .debuggerAddress(dummyDebuggerAddress) + .javaPath(dummyJavaPath) + .build(); + + // then + assertTrue(projectViewTry.isFailure()); + assertEquals( + "doesnt/exist/projectview2.bazelproject file does not exist!", + projectViewTry.getCause().getMessage()); + } + + // targets specific tests + + @Test + public void shouldReturnEmptyTargetsSectionForBuilderWithoutTargets() { + // given & when + var projectViewTry = + ProjectView.builder() + .bazelPath(dummyBazelPathSection) + .debuggerAddress(dummyDebuggerAddress) + .javaPath(dummyJavaPath) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedTargetsSection = new ProjectViewTargetsSection(List.of(), List.of()); + assertEquals(expectedTargetsSection, projectView.getTargets()); + } + + // singleton values specific tests + + @Test + public void shouldReturnEmptySingletonValuesForEmptyBuilder() { + // given & when + var projectViewTry = ProjectView.builder().targets(dummyTargetsSection).build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getBazelPath().isEmpty()); + assertTrue(projectView.getDebuggerAddress().isEmpty()); + assertTrue(projectView.getJavaPath().isEmpty()); + } + + @Test + public void shouldReturnEmptySingletonValuesForBuilderWithEmptyValues() { + // given & when + var projectViewTry = + ProjectView.builder() + .targets(dummyTargetsSection) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getBazelPath().isEmpty()); + assertTrue(projectView.getDebuggerAddress().isEmpty()); + assertTrue(projectView.getJavaPath().isEmpty()); + } + + // project view general tests + + @Test + public void shouldBuildProjectViewWithoutImports() { + // given & when + var projectViewTry = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1", "//included_target2", "//included_target3"), + List.of("//excluded_target1", "//excluded_target2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("127.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path/to/java"))) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of("//included_target1", "//included_target2", "//included_target3"), + List.of("//excluded_target1", "//excluded_target2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedBazelPathSection = new ProjectViewBazelPathSection("path/to/bazel"); + assertEquals(expectedBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("127.0.0.1:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldBuildProjectViewWithEmptyImports() { + // given & when + var projectViewTry = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1", "//included_target2", "//included_target3"), + List.of("//excluded_target1", "//excluded_target2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("127.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path/to/java"))) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of("//included_target1", "//included_target2", "//included_target3"), + List.of("//excluded_target1", "//excluded_target2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = new ProjectViewBazelPathSection("path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("127.0.0.1:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldReturnImportedSingletonValuesAndListValues() { + // given + var importedProjectViewTry = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target1.3"), + List.of("//excluded_target1.1", "//excluded_target1.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path/to/java"))) + .build(); + + // when + var projectViewTry = + ProjectView.builder() + .imports(List.of(importedProjectViewTry)) + .targets(new ProjectViewTargetsSection()) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target1.3"), + List.of("//excluded_target1.1", "//excluded_target1.2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = new ProjectViewBazelPathSection("path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("0.0.0.1:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldReturnSingletonValuesAndListValuesForEmptyImport() { + // given + var importedProjectViewTry = + ProjectView.builder() + .targets(new ProjectViewTargetsSection()) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + // when + var projectViewTry = + ProjectView.builder() + .imports(List.of()) + .targets(new ProjectViewTargetsSection(List.of(), List.of())) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path/to/java"))) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = new ProjectViewTargetsSection(List.of(), List.of()); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = new ProjectViewBazelPathSection("path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("0.0.0.1:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldReturnCurrentSingletonValuesAndCombinedListValues() { + // given + var importedProjectViewTry = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target1.3"), + List.of("//excluded_target1.1", "//excluded_target1.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("imported/path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("imported/path/to/java"))) + .build(); + + // when + var projectViewTry = + ProjectView.builder() + .imports(List.of(importedProjectViewTry)) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target2.1", "//included_target2.2", "//included_target2.3"), + List.of("//excluded_target2.1", "//excluded_target2.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("127.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path/to/java"))) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of( + "//included_target1.1", + "//included_target1.2", + "//included_target1.3", + "//included_target2.1", + "//included_target2.2", + "//included_target2.3"), + List.of( + "//excluded_target1.1", + "//excluded_target1.2", + "//excluded_target2.1", + "//excluded_target2.2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = new ProjectViewBazelPathSection("path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("127.0.0.1:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldReturnLastSingletonValuesAndCombinedListValuesForThreeImports() { + // given + var importedProjectViewTry1 = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target1.3"), + List.of("//excluded_target1.1", "//excluded_target1.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("imported1/path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("imported1/path/to/java"))) + .build(); + + var importedProjectViewTry2 = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target2.1"), List.of("//excluded_target2.1"))) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + var importedProjectViewTry3 = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target3.1", "//included_target3.2"), List.of())) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("imported3/path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.3:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("imported3/path/to/java"))) + .build(); + + // when + var projectViewTry = + ProjectView.builder() + .imports( + List.of(importedProjectViewTry1, importedProjectViewTry2, importedProjectViewTry3)) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target4.1", "//included_target4.2", "//included_target4.3"), + List.of("//excluded_target4.1", "//excluded_target4.2"))) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of( + "//included_target1.1", + "//included_target1.2", + "//included_target1.3", + "//included_target2.1", + "//included_target3.1", + "//included_target3.2", + "//included_target4.1", + "//included_target4.2", + "//included_target4.3"), + List.of( + "//excluded_target1.1", + "//excluded_target1.2", + "//excluded_target2.1", + "//excluded_target4.1", + "//excluded_target4.2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = + new ProjectViewBazelPathSection("imported3/path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("0.0.0.3:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("imported3/path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } + + @Test + public void shouldReturnLastSingletonValuesAndCombinedListValuesForNestedImports() { + // given + var importedProjectViewTry1 = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target1.3"), + List.of("//excluded_target1.1", "//excluded_target1.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("imported1/path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("imported1/path/to/java"))) + .build(); + + var importedProjectViewTry2 = + ProjectView.builder() + .imports(List.of()) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target2.1"), List.of("//excluded_target2.1"))) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + var importedProjectViewTry3 = + ProjectView.builder() + .imports(List.of(importedProjectViewTry1, importedProjectViewTry2)) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target3.1", "//included_target3.2"), List.of())) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("imported3/path/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.3:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("imported3/path/to/java"))) + .build(); + + // when + var projectViewTry = + ProjectView.builder() + .imports(List.of(importedProjectViewTry3)) + .targets( + new ProjectViewTargetsSection( + List.of("//included_target4.1", "//included_target4.2", "//included_target4.3"), + List.of("//excluded_target4.1", "//excluded_target4.2"))) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build(); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectViewTargetsSection = + new ProjectViewTargetsSection( + List.of( + "//included_target1.1", + "//included_target1.2", + "//included_target1.3", + "//included_target2.1", + "//included_target3.1", + "//included_target3.2", + "//included_target4.1", + "//included_target4.2", + "//included_target4.3"), + List.of( + "//excluded_target1.1", + "//excluded_target1.2", + "//excluded_target2.1", + "//excluded_target4.1", + "//excluded_target4.2")); + assertEquals(expectedProjectViewTargetsSection, projectView.getTargets()); + + var expectedProjectViewBazelPathSection = + new ProjectViewBazelPathSection("imported3/path/to/bazel"); + assertEquals(expectedProjectViewBazelPathSection, projectView.getBazelPath().get()); + + var expectedDebuggerAddressSection = new ProjectViewDebuggerAddressSection("0.0.0.3:8000"); + assertEquals(expectedDebuggerAddressSection, projectView.getDebuggerAddress().get()); + + var expectedJavaPathSection = new ProjectViewJavaPathSection("imported3/path/to/java"); + assertEquals(expectedJavaPathSection, projectView.getJavaPath().get()); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD new file mode 100644 index 000000000..ea381fe53 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/BUILD @@ -0,0 +1,25 @@ +load("@rules_java//java:defs.bzl", "java_test") + +java_test( + name = "ProjectViewSingletonSectionTest", + size = "small", + srcs = ["ProjectViewSingletonSectionTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", + ], +) + +java_test( + name = "ProjectViewListSectionTest", + size = "small", + srcs = ["ProjectViewListSectionTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/model/sections", + ], +) diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSectionTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSectionTest.java new file mode 100644 index 000000000..6677a727e --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewListSectionTest.java @@ -0,0 +1,81 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.Collection; +import java.util.List; +import java.util.function.BiFunction; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class ProjectViewListSectionTest { + + private final BiFunction, List, T> sectionConstructor; + + public ProjectViewListSectionTest(BiFunction, List, T> sectionConstructor) { + this.sectionConstructor = sectionConstructor; + } + + @Parameters(name = "{index}: .equals() on a list section for {0}") + public static Collection data() { + return List.of( + new Object[][] { + { + (BiFunction, List, ProjectViewListSection>) + ProjectViewTargetsSection::new + } + }); + } + + @Test + public void shouldReturnTrueForTheSameSectionsWithTheSameValues() { + // given & when + var section1 = + sectionConstructor.apply( + List.of("included_value1", "included_value2"), + List.of("excluded_value1", "excluded_value3", "excluded_value2")); + var section2 = + sectionConstructor.apply( + List.of("included_value2", "included_value1"), + List.of("excluded_value3", "excluded_value2", "excluded_value1")); + + // then + assertEquals(section1, section2); + } + + @Test + public void shouldReturnFalseForTheSameSectionsWithDifferentIncludedValues() { + // given & when + var section1 = + sectionConstructor.apply( + List.of("included_value1", "included_value3"), + List.of("excluded_value1", "excluded_value3", "excluded_value2")); + var section2 = + sectionConstructor.apply( + List.of("included_value2", "included_value1"), + List.of("excluded_value3", "excluded_value2", "excluded_value1")); + + // then + assertNotEquals(section1, section2); + } + + @Test + public void shouldReturnFalseForTheSameSectionsWithDifferentExcludedValues() { + // given & when + var section1 = + sectionConstructor.apply( + List.of("included_value1", "included_value2"), + List.of("excluded_value1", "excluded_value3", "excluded_value2")); + var section2 = + sectionConstructor.apply( + List.of("included_value2", "included_value1"), + List.of("excluded_value3", "excluded_value5", "excluded_value1")); + + // then + assertNotEquals(section1, section2); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSectionTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSectionTest.java new file mode 100644 index 000000000..baedffbcb --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/model/sections/ProjectViewSingletonSectionTest.java @@ -0,0 +1,75 @@ +package org.jetbrains.bsp.bazel.projectview.model.sections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class ProjectViewSingletonSectionTest { + + private final Function sectionConstructor; + private final Function anotherTypeSectionConstructor; + + public ProjectViewSingletonSectionTest( + Function sectionConstructor, Function anotherTypeSectionConstructor) { + this.sectionConstructor = sectionConstructor; + this.anotherTypeSectionConstructor = anotherTypeSectionConstructor; + } + + @Parameters( + name = "{index}: .equals() on a singleton section for {0} and {1} as another type section") + public static Collection data() { + return List.of( + new Object[][] { + { + (Function) ProjectViewBazelPathSection::new, + (Function) ProjectViewDebuggerAddressSection::new + }, + { + (Function) ProjectViewDebuggerAddressSection::new, + (Function) ProjectViewJavaPathSection::new + }, + { + (Function) ProjectViewJavaPathSection::new, + (Function) ProjectViewBazelPathSection::new + } + }); + } + + @Test + public void shouldReturnTrueForTheSameSectionsWithTheSameValues() { + // given & when + var section1 = sectionConstructor.apply("value"); + var section2 = sectionConstructor.apply("value"); + + // then + assertEquals(section1, section2); + } + + @Test + public void shouldReturnFalseForTheSameSectionsWithDifferentValues() { + // given & when + var section1 = sectionConstructor.apply("value"); + var section2 = sectionConstructor.apply("another_value"); + + // then + assertNotEquals(section1, section2); + } + + @Test + public void shouldReturnFalseForDifferentTypes() { + // given & when + var section1 = sectionConstructor.apply("value"); + var section2 = anotherTypeSectionConstructor.apply("value"); + + // then + assertNotEquals(section1, section2); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD index 70a576e1c..701fa1109 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/BUILD @@ -1,18 +1,26 @@ -load("@rules_java//java:defs.bzl", "java_test") +load("@rules_java//java:defs.bzl", "java_library", "java_test") java_test( name = "ProjectViewParserImplTest", size = "small", - srcs = [ - "ProjectViewParserImplTest.java", - "ProjectViewParserMockTestImpl.java", - ], - resources = ["//projectview/src/test/resources/projectview"], + srcs = ["ProjectViewParserImplTest.java"], + resources = ["//projectview/src/test/resources:projectview"], runtime_deps = [ "@maven//:junit_junit", ], deps = [ - "//projectview:model", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser", + "//projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser:parser_mock_test_impl", + ], +) + +java_library( + name = "parser_mock_test_impl", + testonly = 1, + srcs = ["ProjectViewParserMockTestImpl.java"], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser", + "@maven//:com_google_guava_guava", + "@maven//:io_vavr_vavr", ], ) diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImplTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImplTest.java index d7e8356f0..a715e5447 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImplTest.java +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserImplTest.java @@ -1,76 +1,552 @@ package org.jetbrains.bsp.bazel.projectview.parser; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; -import com.google.common.collect.ImmutableList; -import java.io.IOException; -import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDebuggerAddressSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewJavaPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; import org.junit.Before; import org.junit.Test; public class ProjectViewParserImplTest { - private static final DirectoriesSection EXPECTED_DIRECTORIES_SECTION = - new DirectoriesSection( - ImmutableList.of(Paths.get(".")), - ImmutableList.of( - Paths.get("excluded_dir1"), Paths.get("excluded_dir2"), Paths.get("excluded_dir3"))); - - private static final TargetsSection EXPECTED_TARGETS_SECTION = - new TargetsSection( - ImmutableList.of("//included_target1:test1", "//included_target1:test2"), - ImmutableList.of("//excluded_target1:test1")); - - private static final ProjectView EXPECTED_PROJECT_VIEW = - ProjectView.builder() - .directories(EXPECTED_DIRECTORIES_SECTION) - .targets(EXPECTED_TARGETS_SECTION) - .build(); - private ProjectViewParser parser; @Before public void before() { + // given this.parser = new ProjectViewParserMockTestImpl(); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowMissingDirectoriesSection() throws IOException { - Path projectViewFilePath = Paths.get("/projectview/projectViewWithoutDirectories.bazelproject"); - parser.parse(projectViewFilePath); + // ProjectView parse(projectViewFilePath) + + @Test + public void shouldReturnFailureForNotExistingFile() { + // given + var projectViewFilePath = Paths.get("/does/not/exist.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isFailure()); + assertEquals( + "/does/not/exist.bazelproject file does not exist!", + projectViewTry.getCause().getMessage()); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowMissingTargetsSection() throws IOException { - Path projectViewFilePath = Paths.get("/projectview/projectViewWithoutTargets.bazelproject"); - parser.parse(projectViewFilePath); + @Test + public void shouldReturnFailureForNotExistingImportedFile() { + // given + var projectViewFilePath = Paths.get("/projectview/file9ImportsNotExisting.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + System.out.println(projectViewTry); + // then + assertTrue(projectViewTry.isFailure()); + assertEquals( + "/projectview/does/not/exist.bazelproject file does not exist!", + projectViewTry.getCause().getMessage()); } @Test - public void shouldParseFile() throws IOException { - Path projectViewFilePath = Paths.get("/projectview/projectView.bazelproject"); - ProjectView projectView = parser.parse(projectViewFilePath); + public void shouldReturnEmptyTargetsSectionForFileWithoutTargetsSection() { + // given + var projectViewFilePath = Paths.get("/projectview/without/targets.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); - assertEquals(EXPECTED_PROJECT_VIEW, projectView); + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedTargetsSection = new ProjectViewTargetsSection(); + assertEquals(expectedTargetsSection, projectView.getTargets()); } @Test - public void shouldParseFileWithTabs() throws IOException { - Path projectViewFilePath = Paths.get("/projectview/projectViewWithTabs.bazelproject"); - ProjectView projectView = parser.parse(projectViewFilePath); + public void shouldReturnEmptyBazelPathForFileWithoutBazelPathSection() { + // given + var projectViewFilePath = Paths.get("/projectview/without/bazelpath.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); - assertEquals(EXPECTED_PROJECT_VIEW, projectView); + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getBazelPath().isEmpty()); } @Test - public void shouldParseFileWithImport() throws IOException { - Path projectViewFilePath = Paths.get("/projectview/projectViewWithImport.bazelproject"); - ProjectView projectView = parser.parse(projectViewFilePath); + public void shouldReturnEmptyDebuggerAddressForFileWithoutDebuggerAddressSection() { + // given + var projectViewFilePath = Paths.get("/projectview/without/debuggeraddress.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getDebuggerAddress().isEmpty()); + } + + @Test + public void shouldReturnEmptyJavaPathSectionForFileWithoutJavaPathSection() { + // given + var projectViewFilePath = Paths.get("/projectview/without/javapath.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getJavaPath().isEmpty()); + } + + @Test + public void shouldParseEmptyFile() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets(new ProjectViewTargetsSection()) + .bazelPath(Optional.empty()) + .debuggerAddress(Optional.empty()) + .javaPath(Optional.empty()) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithAllSections() { + // given + var projectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2"), + List.of("//excluded_target1.1"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithSingleImportedFileWithoutSingletonValues() { + // given + var projectViewFilePath = Paths.get("/projectview/file4ImportsFile1.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target4.1"), + List.of( + "//excluded_target1.1", "//excluded_target4.1", "//excluded_target4.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithSingleImportedFileWithSingletonValues() { + // given + var projectViewFilePath = Paths.get("/projectview/file7ImportsFile1.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2", "//included_target7.1"), + List.of( + "//excluded_target1.1", "//excluded_target7.1", "//excluded_target7.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path7/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.7:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path7/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithEmptyImportedFile() { + // given + var projectViewFilePath = Paths.get("/projectview/file8ImportsEmpty.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target8.1"), + List.of("//excluded_target8.1", "//excluded_target8.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path8/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.8:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path8/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithThreeImportedFiles() { + // given + var projectViewFilePath = Paths.get("/projectview/file5ImportsFile1File2File3.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of( + "//included_target1.1", + "//included_target1.2", + "//included_target2.1", + "//included_target3.1"), + List.of( + "//excluded_target1.1", + "//excluded_target2.1", + "//excluded_target5.1", + "//excluded_target5.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path3/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.3:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path3/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileWithNestedImportedFiles() { + // given + var projectViewFilePath = Paths.get("/projectview/file6ImportsFile2File3File4.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of( + "//included_target1.1", + "//included_target1.2", + "//included_target2.1", + "//included_target3.1", + "//included_target4.1"), + List.of( + "//excluded_target1.1", + "//excluded_target2.1", + "//excluded_target4.1", + "//excluded_target4.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + // ProjectView parse(projectViewFilePath, defaultProjectViewFilePath) + + @Test + public void shouldReturnFailureForNotExistingDefaultFile() { + // given + var projectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/does/not/exist.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isFailure()); + assertEquals( + "/does/not/exist.bazelproject file does not exist!", + projectViewTry.getCause().getMessage()); + } + + @Test + public void shouldReturnFailureForNotExistingDefaultFileAndNotExistingFile() { + // given + var projectViewFilePath = Paths.get("/does/not/exist.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/does/not/exist.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isFailure()); + assertEquals( + "/does/not/exist.bazelproject file does not exist!", + projectViewTry.getCause().getMessage()); + } + + @Test + public void shouldReturnFile1ForEmptyDefaultFile() { + // given + var projectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2"), + List.of("//excluded_target1.1"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldReturnEmptyTargetsForDefaultFileWithoutTargetsSection() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/without/targets.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedTargetsSection = new ProjectViewTargetsSection(); + assertEquals(expectedTargetsSection, projectView.getTargets()); + } + + @Test + public void shouldReturnEmptyForDefaultFileWithoutBazelPathSection() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/without/bazelpath.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getBazelPath().isEmpty()); + } + + @Test + public void shouldReturnEmptyForDefaultFileWithoutDebuggerAddressSection() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/without/debuggeraddress.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getDebuggerAddress().isEmpty()); + } + + @Test + public void shouldReturnEmptyForDefaultFileWithoutJavaPathSection() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/without/javapath.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + assertTrue(projectView.getJavaPath().isEmpty()); + } + + @Test + public void shouldParseFileAndSkipDefaults() { + // given + var projectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/file2.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2"), + List.of("//excluded_target1.1"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseDefaultsForNotExistingFile() { + // given + var projectViewFilePath = Paths.get("/doesnt/exist.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2"), + List.of("//excluded_target1.1"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileAndUseDefaults() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/file1.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); + + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target1.1", "//included_target1.2"), + List.of("//excluded_target1.1"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path1/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.1:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path1/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); + } + + @Test + public void shouldParseFileAndUseDefaultsWithEmptyImportedFile() { + // given + var projectViewFilePath = Paths.get("/projectview/empty.bazelproject"); + var defaultProjectViewFilePath = Paths.get("/projectview/file8ImportsEmpty.bazelproject"); + + // when + var projectViewTry = parser.parse(projectViewFilePath, defaultProjectViewFilePath); + + // then + assertTrue(projectViewTry.isSuccess()); + var projectView = projectViewTry.get(); - assertEquals(EXPECTED_PROJECT_VIEW, projectView); + var expectedProjectView = + ProjectView.builder() + .targets( + new ProjectViewTargetsSection( + List.of("//included_target8.1"), + List.of("//excluded_target8.1", "//excluded_target8.2"))) + .bazelPath(Optional.of(new ProjectViewBazelPathSection("path8/to/bazel"))) + .debuggerAddress(Optional.of(new ProjectViewDebuggerAddressSection("0.0.0.8:8000"))) + .javaPath(Optional.of(new ProjectViewJavaPathSection("path8/to/java"))) + .build() + .get(); + assertEquals(expectedProjectView, projectView); } } diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserMockTestImpl.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserMockTestImpl.java index 5d3134c5b..67a3bd590 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserMockTestImpl.java +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/ProjectViewParserMockTestImpl.java @@ -2,21 +2,48 @@ import com.google.common.base.Charsets; import com.google.common.io.CharStreams; +import io.vavr.control.Try; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Path; +import java.util.Optional; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; public class ProjectViewParserMockTestImpl extends ProjectViewParserImpl { @Override - public ProjectView parse(Path projectViewPath) throws IOException { - InputStream inputStream = - ProjectViewParserMockTestImpl.class.getResourceAsStream(projectViewPath.toString()); + public Try parse(Path projectViewFilePath, Path defaultProjectViewFilePath) { + return readFileContent(defaultProjectViewFilePath) + .flatMap( + defaultProjectViewFileContent -> + parseWithDefault(projectViewFilePath, defaultProjectViewFileContent)); + } + + private Try parseWithDefault( + Path projectViewFilePath, String defaultProjectViewFileContent) { + return readFileContent(projectViewFilePath) + .flatMap( + projectViewFileContent -> parse(projectViewFileContent, defaultProjectViewFileContent)) + .orElse(parse(defaultProjectViewFileContent)); + } + + @Override + public Try parse(Path projectViewFilePath) { + return readFileContent(projectViewFilePath).flatMap(this::parse); + } + + private Try readFileContent(Path filePath) { // we read file content instead of passing plain file due to bazel resources packaging - String fileContent = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); + var inputStream = ProjectViewParserMockTestImpl.class.getResourceAsStream(filePath.toString()); + + return Optional.ofNullable(inputStream) + .map(this::readInputStream) + .orElse(Try.failure(new IOException(filePath + " file does not exist!"))); + } - return parse(fileContent); + private Try readInputStream(InputStream inputStream) { + return Try.success(new InputStreamReader(inputStream, Charsets.UTF_8)) + .mapTry(CharStreams::toString); } } diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD similarity index 51% rename from projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD rename to projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD index 0ba1eaee5..e20ed54a6 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/BUILD +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/BUILD @@ -1,29 +1,25 @@ load("@rules_java//java:defs.bzl", "java_test") java_test( - name = "DirectoriesSectionParserTest", + name = "ProjectViewTargetsSectionParserTest", size = "small", - srcs = ["DirectoriesSectionParserTest.java"], + srcs = ["ProjectViewTargetsSectionParserTest.java"], runtime_deps = [ "@maven//:junit_junit", ], deps = [ - "//projectview:model", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific", ], ) java_test( - name = "TargetsSectionParserTest", + name = "ProjectViewSingletonSectionParserTest", size = "small", - srcs = ["TargetsSectionParserTest.java"], + srcs = ["ProjectViewSingletonSectionParserTest.java"], runtime_deps = [ "@maven//:junit_junit", ], deps = [ - "//projectview:model", "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections", - "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific", ], ) diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParserTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParserTest.java new file mode 100644 index 000000000..af8e8fdf4 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewSingletonSectionParserTest.java @@ -0,0 +1,221 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewDebuggerAddressSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewJavaPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewSingletonSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class ProjectViewSingletonSectionParserTest { + + private final ProjectViewSingletonSectionParser parser; + private final Function sectionConstructor; + private final String sectionName; + + public ProjectViewSingletonSectionParserTest( + ProjectViewSingletonSectionParser parser, Function sectionConstructor) { + this.parser = parser; + this.sectionConstructor = sectionConstructor; + + this.sectionName = parser.sectionName; + } + + @Parameters(name = "{index}: ProjectViewSingletonSectionParserTest for {0}") + public static Collection data() { + return List.of( + new Object[][] { + { + new ProjectViewBazelPathSectionParser(), + (Function) ProjectViewBazelPathSection::new + }, + { + new ProjectViewDebuggerAddressSectionParser(), + (Function) ProjectViewDebuggerAddressSection::new + }, + { + new ProjectViewJavaPathSectionParser(), + (Function) ProjectViewJavaPathSection::new + } + }); + } + + // T parse(rawSection) + + @Test + public void shouldReturnFailureForWrongSectionName() { + // given + var rawSection = new ProjectViewRawSection("wrongsection", "value"); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isFailure()); + + assertEquals( + "Project view parsing failed! Expected '" + + sectionName + + "' section name, got 'wrongsection'!", + sectionTry.getCause().getMessage()); + } + + @Test + public void shouldReturnEmptyForEmptySectionBody() { + // given + var rawSection = new ProjectViewRawSection(sectionName, ""); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + assertFalse(section.isPresent()); + } + + @Test + public void shouldReturnSectionWithTrimmedValue() { + // given + var rawSection = new ProjectViewRawSection(sectionName, " value"); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = sectionConstructor.apply("value"); + assertEquals(expectedSection, section.get()); + } + + @Test + public void shouldReturnSectionWithTrimmedValueWithSpaces() { + // given + var rawSection = new ProjectViewRawSection(sectionName, " value with space 123 \t\n"); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = sectionConstructor.apply("value with space 123"); + assertEquals(expectedSection, section.get()); + } + + // T parse(rawSections) + + @Test + public void shouldReturnLastSectionWithoutExplicitDefault() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "value1"); + var rawSection2 = new ProjectViewRawSection(sectionName, " value2\n"); + var rawSection3 = new ProjectViewRawSection("another_section2", "\tvalue3\n"); + var rawSection4 = new ProjectViewRawSection(sectionName, " value4\n "); + var rawSection5 = new ProjectViewRawSection("another_section3", "\tvalue5\n"); + + var rawSections = + new ProjectViewRawSections( + List.of(rawSection1, rawSection2, rawSection3, rawSection4, rawSection5)); + + // when + var section = parser.parse(rawSections); + + // then + var expectedSection = sectionConstructor.apply("value4"); + assertEquals(expectedSection, section.get()); + } + + @Test + public void shouldReturnEmptyIfSectionDoesntExist() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "value1"); + var rawSection2 = new ProjectViewRawSection("another_section2", " value2"); + var rawSection3 = new ProjectViewRawSection("another_section3", "\tvalue3\n"); + + var rawSections = new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3)); + + // when + var section = parser.parse(rawSections); + + // then + assertFalse(section.isPresent()); + } + + // T parseOrDefault(rawSections, defaultValue) + + @Test + public void shouldReturnLastSection() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "value1"); + var rawSection2 = new ProjectViewRawSection(sectionName, " value2\n"); + var rawSection3 = new ProjectViewRawSection("another_section2", "\tvalue3\n"); + var rawSection4 = new ProjectViewRawSection(sectionName, " value4\n \t"); + var rawSection5 = new ProjectViewRawSection("another_section3", "\tvalue5\n"); + + var rawSections = + new ProjectViewRawSections( + List.of(rawSection1, rawSection2, rawSection3, rawSection4, rawSection5)); + + var defaultBazelPathSection = sectionConstructor.apply("default_value"); + + // when + var section = parser.parseOrDefault(rawSections, Optional.of(defaultBazelPathSection)); + + // then + var expectedSection = sectionConstructor.apply("value4"); + assertEquals(expectedSection, section.get()); + } + + @Test + public void shouldReturnDefaultValueIfSectionDoesntExist() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "value1"); + var rawSection2 = new ProjectViewRawSection("another_section2", " value2"); + var rawSection3 = new ProjectViewRawSection("another_section3", "\tvalue3\n"); + + var rawSections = new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3)); + + var defaultBazelPathSection = sectionConstructor.apply("default_value"); + + // when + var section = parser.parseOrDefault(rawSections, Optional.of(defaultBazelPathSection)); + + // then + var expectedBazelPathSection = sectionConstructor.apply("default_value"); + assertEquals(expectedBazelPathSection, section.get()); + } + + @Test + public void shouldReturnEmptyDefaultValueIfSectionDoesntExist() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "value1"); + var rawSection2 = new ProjectViewRawSection("another_section2", " value2"); + var rawSection3 = new ProjectViewRawSection("another_section3", "\tvalue3\n"); + + var rawSections = new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3)); + + // when + var section = parser.parseOrDefault(rawSections, Optional.empty()); + + // then + assertFalse(section.isPresent()); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParserTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParserTest.java new file mode 100644 index 000000000..52b70e420 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/ProjectViewTargetsSectionParserTest.java @@ -0,0 +1,217 @@ +package org.jetbrains.bsp.bazel.projectview.parser.sections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSection; +import org.jetbrains.bsp.bazel.projectview.parser.splitter.ProjectViewRawSections; +import org.junit.Before; +import org.junit.Test; + +public class ProjectViewTargetsSectionParserTest { + + private ProjectViewTargetsSectionParser parser; + + @Before + public void before() { + // given + this.parser = new ProjectViewTargetsSectionParser(); + } + + // ProjectViewTargetsSection parse(rawSection) + + @Test + public void shouldReturnFailureForWrongSectionName() { + // given + var rawSection = new ProjectViewRawSection("wrongsection", "-bodyelement"); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isFailure()); + + assertEquals( + "Project view parsing failed! Expected 'targets' section name, got 'wrongsection'!", + sectionTry.getCause().getMessage()); + } + + @Test + public void shouldParseEmptySectionBody() { + // given + var rawSection = new ProjectViewRawSection("targets", ""); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = new ProjectViewTargetsSection(List.of(), List.of()); + assertEquals(expectedSection, section); + } + + @Test + public void shouldParseIncludedTargets() { + // given + var sectionBody = + " //test_included1:test1\n\t//:test_included1:test2\n//:test_included2:test1\n\n"; + var rawSection = new ProjectViewRawSection("targets", sectionBody); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = + new ProjectViewTargetsSection( + List.of("//test_included1:test1", "//:test_included1:test2", "//:test_included2:test1"), + List.of()); + assertEquals(expectedSection, section); + } + + @Test + public void shouldParseExcludedTargets() { + // given + var sectionBody = + " -//test_excluded1:test1\n\t-//test_excluded1:test2\n-//test_excluded2:test1\n\n"; + var rawSection = new ProjectViewRawSection("targets", sectionBody); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = + new ProjectViewTargetsSection( + List.of(), + List.of("//test_excluded1:test1", "//test_excluded1:test2", "//test_excluded2:test1")); + assertEquals(expectedSection, section); + } + + @Test + public void shouldParseIncludedAndExcludedTargets() { + // given + var sectionBody = + " -//test_excluded1:test1\n\t//test_included1:test1\n-//test_excluded1:test2\n\n"; + var rawSection = new ProjectViewRawSection("targets", sectionBody); + + // when + var sectionTry = parser.parse(rawSection); + + // then + assertTrue(sectionTry.isSuccess()); + var section = sectionTry.get(); + + var expectedSection = + new ProjectViewTargetsSection( + List.of("//test_included1:test1"), + List.of("//test_excluded1:test1", "//test_excluded1:test2")); + assertEquals(expectedSection, section); + } + + // ProjectViewTargetsSection parse(rawSections) + + @Test + public void shouldReturnEmptySectionIfThereIsNoSectionForParseWithoutDefault() { + // given + var sectionBody = + " -//test_excluded1:test1\n\t//test_included1:test1\n-//test_excluded1:test2\n\n"; + var rawSection1 = new ProjectViewRawSection("another_section1", sectionBody); + var rawSection2 = new ProjectViewRawSection("another_section2", "-bodyelement2"); + var rawSection3 = new ProjectViewRawSection("another_section3", "-bodyelement3"); + + var rawSections = new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3)); + + // when + var section = parser.parse(rawSections); + + // then + var expectedSection = new ProjectViewTargetsSection(); + assertEquals(expectedSection, section); + } + + @Test + public void shouldParseAllTargetsSectionFromListWithoutDefault() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "-bodyelement1"); + var rawSection2 = + new ProjectViewRawSection("targets", " -//test_excluded1:test1\n-//test_excluded1:test2"); + var rawSection3 = new ProjectViewRawSection("another_section2", "-bodyelement2"); + var rawSection4 = new ProjectViewRawSection("targets", "\n\t//test_included1:test1\n\n\n"); + + var rawSections = + new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3, rawSection4)); + + // when + var section = parser.parse(rawSections); + + // then + var expectedSection = + new ProjectViewTargetsSection( + List.of("//test_included1:test1"), + List.of("//test_excluded1:test1", "//test_excluded1:test2")); + assertEquals(expectedSection, section); + } + + // ProjectViewTargetsSection parseOrDefault(rawSections, defaultValue) + + @Test + public void shouldParseAllTargetsSectionFromList() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "-bodyelement1"); + var rawSection2 = + new ProjectViewRawSection("targets", " -//test_excluded1:test1\n-//test_excluded1:test2"); + var rawSection3 = new ProjectViewRawSection("another_section2", "-bodyelement2"); + var rawSection4 = new ProjectViewRawSection("targets", "\n\t//test_included1:test1\n\n\n"); + + var rawSections = + new ProjectViewRawSections(List.of(rawSection1, rawSection2, rawSection3, rawSection4)); + + var defaultTargetsSection = + new ProjectViewTargetsSection( + List.of("//default_test_included1:test1"), + List.of("//default_excluded_test:test1", "//default_excluded_test:test1")); + + // when + var section = parser.parseOrDefault(rawSections, defaultTargetsSection); + + // then + var expectedSection = + new ProjectViewTargetsSection( + List.of("//test_included1:test1"), + List.of("//test_excluded1:test1", "//test_excluded1:test2")); + assertEquals(expectedSection, section); + } + + @Test + public void shouldReturnDefaultForNoTargetsSectionInList() { + // given + var rawSection1 = new ProjectViewRawSection("another_section1", "-bodyelement1"); + var rawSection2 = new ProjectViewRawSection("another_section2", "-bodyelement2"); + + var rawSections = new ProjectViewRawSections(List.of(rawSection1, rawSection2)); + + var defaultTargetsSection = + new ProjectViewTargetsSection( + List.of("//default_test_included1:test1"), + List.of("//default_excluded_test:test1", "//default_excluded_test:test1")); + + // when + var section = parser.parseOrDefault(rawSections, defaultTargetsSection); + + // then + var expectedSection = + new ProjectViewTargetsSection( + List.of("//default_test_included1:test1"), + List.of("//default_excluded_test:test1", "//default_excluded_test:test1")); + assertEquals(expectedSection, section); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParserTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParserTest.java deleted file mode 100644 index 2acd84f6e..000000000 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/DirectoriesSectionParserTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser.sections.specific; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableList; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewSectionParser; -import org.junit.Before; -import org.junit.Test; - -public class DirectoriesSectionParserTest { - - private ProjectViewSectionParser parser; - - @Before - public void before() { - this.parser = new DirectoriesSectionParser(); - } - - @Test - public void shouldRecognizeSectionHeader() { - String directoriesSectionHeader = "directories"; - - assertTrue(parser.isSectionParsable(directoriesSectionHeader)); - } - - @Test - public void shouldNotRecognizeInvalidSectionHeader() { - String directoriesSectionHeader = "invalid_header"; - - assertFalse(parser.isSectionParsable(directoriesSectionHeader)); - } - - @Test - public void shouldParseIncludedDirectories() { - String entryBody = "test_included1 test_included2 test_included3 \n"; - - DirectoriesSection section = parser.parse(entryBody); - - List expectedIncludedPaths = - ImmutableList.of( - Paths.get("test_included1"), Paths.get("test_included2"), Paths.get("test_included3")); - List expectedExcludedPaths = ImmutableList.of(); - - assertEquals(expectedIncludedPaths, section.getIncludedDirectories()); - assertEquals(expectedExcludedPaths, section.getExcludedDirectories()); - } - - @Test - public void shouldParseExcludedDirectories() { - String entryBody = "-test_excluded1 -test_excluded2 -test_excluded3 \n"; - - DirectoriesSection section = parser.parse(entryBody); - - List expectedIncludedPaths = ImmutableList.of(); - List expectedExcludedPaths = - ImmutableList.of( - Paths.get("test_excluded1"), Paths.get("test_excluded2"), Paths.get("test_excluded3")); - - assertEquals(expectedIncludedPaths, section.getIncludedDirectories()); - assertEquals(expectedExcludedPaths, section.getExcludedDirectories()); - } - - @Test - public void shouldParseIncludedAndExcludedDirectories() { - String entryBody = "test_included1 -test_excluded1 test_included2 \n-test_excluded2\n"; - - DirectoriesSection section = parser.parse(entryBody); - - List expectedIncludedPaths = - ImmutableList.of(Paths.get("test_included1"), Paths.get("test_included2")); - List expectedExcludedPaths = - ImmutableList.of(Paths.get("test_excluded1"), Paths.get("test_excluded2")); - - assertEquals(expectedIncludedPaths, section.getIncludedDirectories()); - assertEquals(expectedExcludedPaths, section.getExcludedDirectories()); - } -} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParserTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParserTest.java deleted file mode 100644 index 0349cb7c4..000000000 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/sections/specific/TargetsSectionParserTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.jetbrains.bsp.bazel.projectview.parser.sections.specific; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.google.common.collect.ImmutableList; -import java.util.List; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; -import org.jetbrains.bsp.bazel.projectview.parser.sections.ProjectViewSectionParser; -import org.junit.Before; -import org.junit.Test; - -public class TargetsSectionParserTest { - - private ProjectViewSectionParser parser; - - @Before - public void before() { - this.parser = new TargetsSectionParser(); - } - - @Test - public void shouldRecognizeSectionHeader() { - String directoriesSectionHeader = "targets"; - - assertTrue(parser.isSectionParsable(directoriesSectionHeader)); - } - - @Test - public void shouldNotRecognizeInvalidSectionHeader() { - String directoriesSectionHeader = "invalid_header"; - - assertFalse(parser.isSectionParsable(directoriesSectionHeader)); - } - - @Test - public void shouldParseIncludedTargets() { - String entryBody = - " //test_included1:test1\n //:test_included1:test2\n //:test_included2:test1\n\n"; - - TargetsSection section = parser.parse(entryBody); - - List expectedIncludedTargets = - ImmutableList.of( - "//test_included1:test1", "//:test_included1:test2", "//:test_included2:test1"); - List expectedExcludedTargets = ImmutableList.of(); - - assertEquals(expectedIncludedTargets, section.getIncludedTargets()); - assertEquals(expectedExcludedTargets, section.getExcludedTargets()); - } - - @Test - public void shouldParseExcludedTargets() { - String entryBody = - " -//test_excluded1:test1\n -//test_excluded1:test2\n -//test_excluded2:test1\n\n"; - - TargetsSection section = parser.parse(entryBody); - - List expectedIncludedTargets = ImmutableList.of(); - List expectedExcludedTargets = - ImmutableList.of( - "//test_excluded1:test1", "//test_excluded1:test2", "//test_excluded2:test1"); - - assertEquals(expectedIncludedTargets, section.getIncludedTargets()); - assertEquals(expectedExcludedTargets, section.getExcludedTargets()); - } - - @Test - public void shouldParseIncludedAndExcludedTargets() { - String entryBody = - " -//test_excluded1:test1\n //test_included1:test1\n -//test_excluded1:test2\n\n"; - - TargetsSection section = parser.parse(entryBody); - - List expectedIncludedTargets = ImmutableList.of("//test_included1:test1"); - List expectedExcludedTargets = - ImmutableList.of("//test_excluded1:test1", "//test_excluded1:test2"); - - assertEquals(expectedIncludedTargets, section.getIncludedTargets()); - assertEquals(expectedExcludedTargets, section.getExcludedTargets()); - } -} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD index 554ae865d..35b28e5bf 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/BUILD @@ -1,5 +1,30 @@ load("@rules_java//java:defs.bzl", "java_test") +java_test( + name = "ProjectViewRawSectionsTest", + size = "small", + srcs = ["ProjectViewRawSectionsTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter", + "@maven//:org_apache_commons_commons_collections4", + ], +) + +java_test( + name = "ProjectViewRawSectionTest", + size = "small", + srcs = ["ProjectViewRawSectionTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//projectview/src/main/java/org/jetbrains/bsp/bazel/projectview/parser/splitter", + ], +) + java_test( name = "ProjectViewSectionSplitterTest", size = "small", diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionTest.java new file mode 100644 index 000000000..db32fda81 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionTest.java @@ -0,0 +1,55 @@ +package org.jetbrains.bsp.bazel.projectview.parser.splitter; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class ProjectViewRawSectionTest { + + private final ProjectViewRawSection section; + private final String nameToCompare; + private final boolean expectedComparisonResult; + + public ProjectViewRawSectionTest( + ProjectViewRawSection section, String nameToCompare, boolean expectedComparisonResult) { + this.section = section; + this.nameToCompare = nameToCompare; + this.expectedComparisonResult = expectedComparisonResult; + } + + @Parameters(name = "{index}: {0}.shouldCompareByName({1}) should equals {2}") + public static Collection data() { + return List.of( + new Object[][] { + { + // given + new ProjectViewRawSection("name", "body"), + "anothername", + // then + false + }, + { + // given + new ProjectViewRawSection("name", "body"), + "name", + // then + true + } + }); + } + + @Test + public void shouldCompareByName() { + // when + var result = section.hasName(nameToCompare); + + // then + assertEquals(expectedComparisonResult, result); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionsTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionsTest.java new file mode 100644 index 000000000..e975ede34 --- /dev/null +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewRawSectionsTest.java @@ -0,0 +1,138 @@ +package org.jetbrains.bsp.bazel.projectview.parser.splitter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.collections4.CollectionUtils; +import org.junit.Test; + +public class ProjectViewRawSectionsTest { + + @Test + public void shouldReturnEmptyForEmptySections() { + // given + var sections = List.of(); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionWithName = projectViewRawSections.getLastSectionWithName("doesntexist"); + + // then + assertFalse(sectionWithName.isPresent()); + } + + @Test + public void shouldReturnEmptyIfSectionDoesntExist() { + // given + var sections = + List.of( + new ProjectViewRawSection("name1", "body1"), + new ProjectViewRawSection("name2", "body2"), + new ProjectViewRawSection("name3", "body3")); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionWithName = projectViewRawSections.getLastSectionWithName("doesntexist"); + + // then + assertFalse(sectionWithName.isPresent()); + } + + @Test + public void shouldReturnOnlySectionWithName() { + // given + var sections = + List.of( + new ProjectViewRawSection("name1", "body1"), + new ProjectViewRawSection("name2", "body2"), + new ProjectViewRawSection("name3", "body3")); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionWithName = projectViewRawSections.getLastSectionWithName("name1"); + + // then + var expectedSection = new ProjectViewRawSection("name1", "body1"); + assertEquals(expectedSection, sectionWithName.get()); + } + + @Test + public void shouldReturnLastSectionWithName() { + // given + var sections = + List.of( + new ProjectViewRawSection("name1", "body1.1"), + new ProjectViewRawSection("name2", "body2"), + new ProjectViewRawSection("name1", "body1.2"), + new ProjectViewRawSection("name1", "body1.3"), + new ProjectViewRawSection("name3", "body3")); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionWithName = projectViewRawSections.getLastSectionWithName("name1"); + + // then + var expectedSection = new ProjectViewRawSection("name1", "body1.3"); + assertEquals(expectedSection, sectionWithName.get()); + } + + @Test + public void shouldReturnEmptyListForEmptySections() { + // given + var sections = List.of(); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionsWithName = + projectViewRawSections.getAllWithName("doesntexist").collect(Collectors.toList()); + + // then + assertTrue(sectionsWithName.isEmpty()); + } + + @Test + public void shouldReturnEmptyListIfSectionDoesntExist() { + // given + var sections = + List.of( + new ProjectViewRawSection("name1", "body1"), + new ProjectViewRawSection("name2", "body2"), + new ProjectViewRawSection("name3", "body3")); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionsWithName = + projectViewRawSections.getAllWithName("doesntexist").collect(Collectors.toList()); + + // then + assertTrue(sectionsWithName.isEmpty()); + } + + @Test + public void shouldReturnAllSectionsWithName() { + // given + var sections = + List.of( + new ProjectViewRawSection("name1", "body1.1"), + new ProjectViewRawSection("name2", "body2"), + new ProjectViewRawSection("name1", "body1.2"), + new ProjectViewRawSection("name1", "body1.3"), + new ProjectViewRawSection("name3", "body3")); + var projectViewRawSections = new ProjectViewRawSections(sections); + + // when + var sectionsWithName = + projectViewRawSections.getAllWithName("name1").collect(Collectors.toList()); + + // then + var expectedSections = + List.of( + new ProjectViewRawSection("name1", "body1.1"), + new ProjectViewRawSection("name1", "body1.3"), + new ProjectViewRawSection("name1", "body1.2")); + assertTrue(CollectionUtils.isEqualCollection(expectedSections, sectionsWithName)); + } +} diff --git a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitterTest.java b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitterTest.java index 1e4432762..78accd999 100644 --- a/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitterTest.java +++ b/projectview/src/test/java/org/jetbrains/bsp/bazel/projectview/parser/splitter/ProjectViewSectionSplitterTest.java @@ -2,66 +2,241 @@ import static org.junit.Assert.assertEquals; -import com.google.common.collect.ImmutableList; +import java.util.Collection; import java.util.List; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(value = Parameterized.class) public class ProjectViewSectionSplitterTest { - @Test - public void shouldParseEmptyFile() { - String emptyContent = ""; + private final String fileContent; + private final ProjectViewRawSections expectedSections; - List result = ProjectViewSectionSplitter.split(emptyContent); - List expectedResult = ImmutableList.of(); + public ProjectViewSectionSplitterTest( + String fileContent, ProjectViewRawSections expectedSections) { + this.fileContent = fileContent; + this.expectedSections = expectedSections; + } - assertEquals(expectedResult, result); + @Parameters(name = "{index}: ProjectViewSectionSplitter.split({0}) should equals {1}") + public static Collection data() { + return List.of( + new Object[][] { + { + // given - empty + "", + // then + new ProjectViewRawSections(List.of()) + }, + { + // given - empty section + "section: ", + // then + new ProjectViewRawSections(List.of(new ProjectViewRawSection("section", " "))) + }, + { + // given - no colon + "import path/to/file.bazelproject\n", + // then + new ProjectViewRawSections( + List.of(new ProjectViewRawSection("import", "path/to/file.bazelproject\n"))) + }, + { + // given - new line in front + "\nimport path/to/file.bazelproject\n", + // then + new ProjectViewRawSections( + List.of(new ProjectViewRawSection("import", "path/to/file.bazelproject\n"))) + }, + { + // given - multiple elements + "section: " + + "section_included_element1 " + + "-section_excluded_element1 " + + "-section_excluded_element2 " + + "-section_excluded_element3 " + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection( + "section", + " " + + "section_included_element1 " + + "-section_excluded_element1 " + + "-section_excluded_element2 " + + "-section_excluded_element3 " + + "\n"))) + }, + { + // given - new line in front and multiple elements + "\n\nsection: " + + "section_included_element1 " + + "-section_excluded_element1 " + + "-section_excluded_element2 " + + "-section_excluded_element3 " + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection( + "section", + " " + + "section_included_element1 " + + "-section_excluded_element1 " + + "-section_excluded_element2 " + + "-section_excluded_element3 " + + "\n"))) + }, + { + // given - multiple elements with whitespaces in front and new lines + "section:\n" + + "\tsection_included_element1\n" + + "-section_excluded_element1\n" + + "\tsection_included_element2\n" + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection( + "section", + "\n" + + "\tsection_included_element1\n" + + "-section_excluded_element1\n" + + "\tsection_included_element2\n" + + "\n"))) + }, + { + // given - single element + "section: section_element\n", + // then + new ProjectViewRawSections( + List.of(new ProjectViewRawSection("section", " section_element\n"))) + }, + { + // given - element with dots and colon + "section: 1.2.3.4:8080\n", + // then + new ProjectViewRawSections( + List.of(new ProjectViewRawSection("section", " 1.2.3.4:8080\n"))) + }, + { + // given - comment + " # comment", + // then + new ProjectViewRawSections(List.of()) + }, + { + // given - commented out section + "#section: value\n", + // then + new ProjectViewRawSections(List.of()) + }, + { + // given - multiple elements with new lines and comments + " # comment 1\n\n" + + "section:\n" + + "\tsection_included_element1\n" + + "#comment2\n" + + "-section_excluded_element1 # comment 3 \n" + + "\tsection_included_element2\n" + + "\t #comment 4\n\n" + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection( + "section", + "\n" + + "\tsection_included_element1\n" + + "\n" + + "-section_excluded_element1 \n" + + "\tsection_included_element2\n" + + "\t \n\n" + + "\n"))) + }, + { + // given - multiple elements with whitespaces in front and new lines + "section:\n" + + "\tsection_included_element1\n\n\n" + + "-section_excluded_element1\n" + + "\tsection_included_element2\n" + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection( + "section", + "\n" + + "\tsection_included_element1\n\n\n" + + "-section_excluded_element1\n" + + "\tsection_included_element2\n" + + "\n"))) + }, + { + // given - full file + "import path/to/file.bazelproject" + + "\n" + + "# some comment\n" + + "section1: " + + "section1_included_element1 " + + "-section1_excluded_element1 " + + "-section1_excluded_element2 " + + "-section1_excluded_element3 " + + "\n" + + "section2:\n" + + " section2_included_element1\n" + + " -section2_excluded_element1\n" + + " # commented_out_target\n" + + "\tsection2_included_element2\n" + + "\n" + + "section3: section3_element\n" + + "\n\n\n" + + "sectionA:\n" + + " --sectionA_element_flag\n" + + "# commented_out_section: comment\n\n" + + "\n" + + "sectionb:" + + "*sectionb_element1\n" + + "*sectionb_element2\n" + + "\n", + // then + new ProjectViewRawSections( + List.of( + new ProjectViewRawSection("import", "path/to/file.bazelproject\n\n"), + new ProjectViewRawSection( + "section1", + " " + + "section1_included_element1 " + + "-section1_excluded_element1 " + + "-section1_excluded_element2 " + + "-section1_excluded_element3 " + + "\n"), + new ProjectViewRawSection( + "section2", + "\n" + + " section2_included_element1\n" + + " -section2_excluded_element1\n" + + " \n" + + "\tsection2_included_element2\n" + + "\n"), + new ProjectViewRawSection("section3", " section3_element\n\n\n\n"), + new ProjectViewRawSection("sectionA", "\n --sectionA_element_flag\n\n\n\n"), + new ProjectViewRawSection( + "sectionb", "*sectionb_element1\n*sectionb_element2\n\n"))) + } + }); } @Test - public void shouldParseRegularFile() { - String fileContent = - "import path/to/file.bazelproject" - + "\n" - + "directories: " - + ". " - + "-excluded_dir1 " - + "-excluded_dir2 " - + "-excluded_dir3" - + "\n" - + "targets:\n" - + " //included_target1:test1\n" - + " -//excluded_target1:test1\n" - + " //included_target1:test2\n" - + "\n" - + "workspace_type: not_parsed\n" - + "\n" - + "build_flags:\n" - + " --not_parsed_flag\n" - + "\n" - + "test_sources:\n" - + " *test/not/parsed1/*\n" - + " *test/not/parsed2/*\n" - + "\n"; - - List result = ProjectViewSectionSplitter.split(fileContent); - List expectedResult = - ImmutableList.of( - new ProjectViewRawSection("import", " path/to/file.bazelproject\n"), - new ProjectViewRawSection( - "directories", " . -excluded_dir1 -excluded_dir2 -excluded_dir3\n"), - new ProjectViewRawSection( - "targets", - "\n" - + " //included_target1:test1\n" - + " -//excluded_target1:test1\n" - + " //included_target1:test2\n" - + "\n"), - new ProjectViewRawSection("workspace_type", " not_parsed\n\n"), - new ProjectViewRawSection("build_flags", "\n --not_parsed_flag\n\n"), - new ProjectViewRawSection( - "test_sources", "\n *test/not/parsed1/*\n *test/not/parsed2/*\n\n")); + public void shouldSplitContent() { + // when + var sections = ProjectViewSectionSplitter.getRawSectionsForFileContent(fileContent); - assertEquals(expectedResult, result); + // then + assertEquals(expectedSections, sections); } } diff --git a/projectview/src/test/resources/projectview/BUILD b/projectview/src/test/resources/BUILD similarity index 71% rename from projectview/src/test/resources/projectview/BUILD rename to projectview/src/test/resources/BUILD index 05e93fbed..6da5dd388 100644 --- a/projectview/src/test/resources/projectview/BUILD +++ b/projectview/src/test/resources/BUILD @@ -1,5 +1,5 @@ filegroup( name = "projectview", - srcs = glob(["*.bazelproject"]), + srcs = glob(["**/*.bazelproject"]), visibility = ["//projectview/src/test:__subpackages__"], ) diff --git a/projectview/src/test/resources/projectview/empty.bazelproject b/projectview/src/test/resources/projectview/empty.bazelproject new file mode 100644 index 000000000..888f75eb9 --- /dev/null +++ b/projectview/src/test/resources/projectview/empty.bazelproject @@ -0,0 +1,5 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +not_parsed_section2: not_parsed2.1 diff --git a/projectview/src/test/resources/projectview/file1.bazelproject b/projectview/src/test/resources/projectview/file1.bazelproject new file mode 100644 index 000000000..27ee29205 --- /dev/null +++ b/projectview/src/test/resources/projectview/file1.bazelproject @@ -0,0 +1,20 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +# comment + +targets: + //included_target1.1 # some comment + //included_target1.2 + # //not_included_target1.3 + -//excluded_target1.1 + +not_parsed_section2: not_parsed2.1 + +bazel_path: path1/to/bazel # another comment + +debugger_address: 0.0.0.1:8000 + +# java_path: old/path/to/java +java_path: path1/to/java diff --git a/projectview/src/test/resources/projectview/file2.bazelproject b/projectview/src/test/resources/projectview/file2.bazelproject new file mode 100644 index 000000000..013e44a60 --- /dev/null +++ b/projectview/src/test/resources/projectview/file2.bazelproject @@ -0,0 +1,3 @@ +targets: + //included_target2.1 + -//excluded_target2.1 diff --git a/projectview/src/test/resources/projectview/file3.bazelproject b/projectview/src/test/resources/projectview/file3.bazelproject new file mode 100644 index 000000000..e5d097654 --- /dev/null +++ b/projectview/src/test/resources/projectview/file3.bazelproject @@ -0,0 +1,9 @@ +targets: + + //included_target3.1 + +bazel_path: path3/to/bazel + +debugger_address: 0.0.0.3:8000 + +java_path: path3/to/java diff --git a/projectview/src/test/resources/projectview/file4ImportsFile1.bazelproject b/projectview/src/test/resources/projectview/file4ImportsFile1.bazelproject new file mode 100644 index 000000000..6efdc19b3 --- /dev/null +++ b/projectview/src/test/resources/projectview/file4ImportsFile1.bazelproject @@ -0,0 +1,6 @@ +import /projectview/file1.bazelproject + +targets: + //included_target4.1 + -//excluded_target4.1 + -//excluded_target4.2 diff --git a/projectview/src/test/resources/projectview/file5ImportsFile1File2File3.bazelproject b/projectview/src/test/resources/projectview/file5ImportsFile1File2File3.bazelproject new file mode 100644 index 000000000..9ae8baf8d --- /dev/null +++ b/projectview/src/test/resources/projectview/file5ImportsFile1File2File3.bazelproject @@ -0,0 +1,7 @@ +import /projectview/file1.bazelproject +import /projectview/file2.bazelproject +import /projectview/file3.bazelproject + +targets: + -//excluded_target5.1 + -//excluded_target5.2 diff --git a/projectview/src/test/resources/projectview/file6ImportsFile2File3File4.bazelproject b/projectview/src/test/resources/projectview/file6ImportsFile2File3File4.bazelproject new file mode 100644 index 000000000..b10c50db7 --- /dev/null +++ b/projectview/src/test/resources/projectview/file6ImportsFile2File3File4.bazelproject @@ -0,0 +1,3 @@ +import /projectview/file2.bazelproject +import /projectview/file3.bazelproject +import /projectview/file4ImportsFile1.bazelproject diff --git a/projectview/src/test/resources/projectview/file7ImportsFile1.bazelproject b/projectview/src/test/resources/projectview/file7ImportsFile1.bazelproject new file mode 100644 index 000000000..d7df7864d --- /dev/null +++ b/projectview/src/test/resources/projectview/file7ImportsFile1.bazelproject @@ -0,0 +1,12 @@ +import /projectview/file1.bazelproject + +targets: + //included_target7.1 + -//excluded_target7.1 + -//excluded_target7.2 + +bazel_path: path7/to/bazel + +debugger_address: 0.0.0.7:8000 + +java_path: path7/to/java diff --git a/projectview/src/test/resources/projectview/file8ImportsEmpty.bazelproject b/projectview/src/test/resources/projectview/file8ImportsEmpty.bazelproject new file mode 100644 index 000000000..1ea912801 --- /dev/null +++ b/projectview/src/test/resources/projectview/file8ImportsEmpty.bazelproject @@ -0,0 +1,13 @@ +import /projectview/empty.bazelproject + + +targets: + //included_target8.1 + -//excluded_target8.1 + -//excluded_target8.2 + +bazel_path: path8/to/bazel + +debugger_address: 0.0.0.8:8000 + +java_path: path8/to/java \ No newline at end of file diff --git a/projectview/src/test/resources/projectview/file9ImportsNotExisting.bazelproject b/projectview/src/test/resources/projectview/file9ImportsNotExisting.bazelproject new file mode 100644 index 000000000..a7d24508c --- /dev/null +++ b/projectview/src/test/resources/projectview/file9ImportsNotExisting.bazelproject @@ -0,0 +1,13 @@ +import /projectview/does/not/exist.bazelproject + + +targets: + //included_target9.1 + -//excluded_target9.1 + -//excluded_target9.2 + +bazel_path: path9/to/bazel + +debugger_address: 0.0.0.9:8000 + +java_path: path9/to/java \ No newline at end of file diff --git a/projectview/src/test/resources/projectview/projectView.bazelproject b/projectview/src/test/resources/projectview/projectView.bazelproject deleted file mode 100644 index 293c04670..000000000 --- a/projectview/src/test/resources/projectview/projectView.bazelproject +++ /dev/null @@ -1,15 +0,0 @@ -directories: . -excluded_dir1 -excluded_dir2 -excluded_dir3 - -workspace_type: not_parsed - -targets: - //included_target1:test1 - -//excluded_target1:test1 - //included_target1:test2 - -build_flags: - --not_parsed_flag - -test_sources: - *test/not/parsed1/* - *test/not/parsed2/* diff --git a/projectview/src/test/resources/projectview/projectViewToImport.bazelproject b/projectview/src/test/resources/projectview/projectViewToImport.bazelproject deleted file mode 100644 index 2cf34afbc..000000000 --- a/projectview/src/test/resources/projectview/projectViewToImport.bazelproject +++ /dev/null @@ -1,12 +0,0 @@ -directories: . -excluded_dir1 - -workspace_type: not_parsed1 - -targets: - //included_target1:test1 - -build_flags: - --not_parsed_flag1 - -test_sources: - *test/not/parsed1/* diff --git a/projectview/src/test/resources/projectview/projectViewWithImport.bazelproject b/projectview/src/test/resources/projectview/projectViewWithImport.bazelproject deleted file mode 100644 index a17c1a4eb..000000000 --- a/projectview/src/test/resources/projectview/projectViewWithImport.bazelproject +++ /dev/null @@ -1,7 +0,0 @@ -import /projectview/projectViewToImport.bazelproject - -directories: -excluded_dir2 -excluded_dir3 - -targets: - -//excluded_target1:test1 - //included_target1:test2 diff --git a/projectview/src/test/resources/projectview/projectViewWithTabs.bazelproject b/projectview/src/test/resources/projectview/projectViewWithTabs.bazelproject deleted file mode 100644 index 53142c786..000000000 --- a/projectview/src/test/resources/projectview/projectViewWithTabs.bazelproject +++ /dev/null @@ -1,15 +0,0 @@ -directories: . -excluded_dir1 -excluded_dir2 -excluded_dir3 - -workspace_type: not_parsed - -targets: - //included_target1:test1 - -//excluded_target1:test1 - //included_target1:test2 - -build_flags: - --not_parsed_flag - -test_sources: - *test/not/parsed1/* - *test/not/parsed2/* diff --git a/projectview/src/test/resources/projectview/projectViewWithoutDirectories.bazelproject b/projectview/src/test/resources/projectview/projectViewWithoutDirectories.bazelproject deleted file mode 100644 index 74cd646d0..000000000 --- a/projectview/src/test/resources/projectview/projectViewWithoutDirectories.bazelproject +++ /dev/null @@ -1,13 +0,0 @@ -workspace_type: not_parsed - -targets: - //included_target1:test1 - -//excluded_target1:test1 - //included_target1:test2 - -build_flags: - --not_parsed_flag - -test_sources: - *test/not/parsed1/* - *test/not/parsed2/* diff --git a/projectview/src/test/resources/projectview/projectViewWithoutTargets.bazelproject b/projectview/src/test/resources/projectview/projectViewWithoutTargets.bazelproject deleted file mode 100644 index 3f6beb3f5..000000000 --- a/projectview/src/test/resources/projectview/projectViewWithoutTargets.bazelproject +++ /dev/null @@ -1,10 +0,0 @@ -directories: . -excluded_dir1 -excluded_dir2 -excluded_dir3 - -workspace_type: not_parsed - -build_flags: - --not_parsed_flag - -test_sources: - *test/not/parsed1/* - *test/not/parsed2/* diff --git a/projectview/src/test/resources/projectview/without/bazelpath.bazelproject b/projectview/src/test/resources/projectview/without/bazelpath.bazelproject new file mode 100644 index 000000000..4b63cfc4f --- /dev/null +++ b/projectview/src/test/resources/projectview/without/bazelpath.bazelproject @@ -0,0 +1,15 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +not_parsed_section2: not_parsed2.1 + + +targets: + //included_target1.1 + //included_target1.2 + -//excluded_target1.1 + +debugger_address: 127.0.0.1:8000 + +java_path: path/to/java diff --git a/projectview/src/test/resources/projectview/without/debuggeraddress.bazelproject b/projectview/src/test/resources/projectview/without/debuggeraddress.bazelproject new file mode 100644 index 000000000..d2d9fece4 --- /dev/null +++ b/projectview/src/test/resources/projectview/without/debuggeraddress.bazelproject @@ -0,0 +1,15 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +not_parsed_section2: not_parsed2.1 + + +targets: + //included_target1.1 + //included_target1.2 + -//excluded_target1.1 + +bazel_path: path/to/bazel + +java_path: path/to/java \ No newline at end of file diff --git a/projectview/src/test/resources/projectview/without/javapath.bazelproject b/projectview/src/test/resources/projectview/without/javapath.bazelproject new file mode 100644 index 000000000..06e2f3258 --- /dev/null +++ b/projectview/src/test/resources/projectview/without/javapath.bazelproject @@ -0,0 +1,15 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +not_parsed_section2: not_parsed2.1 + + +targets: + //included_target1.1 + //included_target1.2 + -//excluded_target1.1 + +bazel_path: path/to/bazel + +debugger_address: 127.0.0.1:8000 diff --git a/projectview/src/test/resources/projectview/without/targets.bazelproject b/projectview/src/test/resources/projectview/without/targets.bazelproject new file mode 100644 index 000000000..58f7bd1d7 --- /dev/null +++ b/projectview/src/test/resources/projectview/without/targets.bazelproject @@ -0,0 +1,12 @@ +not_parsed_section1: + not_parsed1.1 + not_parsed2.2 + +not_parsed_section2: not_parsed2.1 + + +bazel_path: path/to/bazel + +debugger_address: 127.0.0.1:8000 + +java_path: path/to/java diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/BUILD b/server/src/main/java/org/jetbrains/bsp/bazel/server/BUILD index 8792393b0..dfb552b6b 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/BUILD +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/BUILD @@ -16,11 +16,13 @@ java_library( "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers", "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers", "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services", + "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils", "@io_bazel//src/main/protobuf:build_java_proto", "@io_bazel//third_party/grpc:grpc-jar", "@maven//:ch_epfl_scala_bsp4j", "@maven//:com_google_code_gson_gson", "@maven//:com_google_guava_guava", + "@maven//:io_vavr_vavr", "@maven//:org_apache_logging_log4j_log4j_api", "@maven//:org_apache_logging_log4j_log4j_core", "@maven//:org_eclipse_lsp4j_org_eclipse_lsp4j_jsonrpc", diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/BazelBspServer.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/BazelBspServer.java index 4d6f973cf..2b3078b3d 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/BazelBspServer.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/BazelBspServer.java @@ -4,8 +4,10 @@ import ch.epfl.scala.bsp4j.BuildServer; import ch.epfl.scala.bsp4j.CppBuildServer; import ch.epfl.scala.bsp4j.JavaBuildServer; +import ch.epfl.scala.bsp4j.JvmBuildServer; import ch.epfl.scala.bsp4j.ScalaBuildServer; import io.grpc.ServerBuilder; +import java.nio.file.Paths; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.jetbrains.bsp.bazel.bazelrunner.BazelDataResolver; import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; @@ -20,6 +22,7 @@ import org.jetbrains.bsp.bazel.server.bsp.impl.BuildServerImpl; import org.jetbrains.bsp.bazel.server.bsp.impl.CppBuildServerImpl; import org.jetbrains.bsp.bazel.server.bsp.impl.JavaBuildServerImpl; +import org.jetbrains.bsp.bazel.server.bsp.impl.JvmBuildServerImpl; import org.jetbrains.bsp.bazel.server.bsp.impl.ScalaBuildServerImpl; import org.jetbrains.bsp.bazel.server.bsp.managers.BazelBspAspectsManager; import org.jetbrains.bsp.bazel.server.bsp.managers.BazelBspCompilationManager; @@ -29,7 +32,9 @@ import org.jetbrains.bsp.bazel.server.bsp.services.BuildServerService; import org.jetbrains.bsp.bazel.server.bsp.services.CppBuildServerService; import org.jetbrains.bsp.bazel.server.bsp.services.JavaBuildServerService; +import org.jetbrains.bsp.bazel.server.bsp.services.JvmBuildServerService; import org.jetbrains.bsp.bazel.server.bsp.services.ScalaBuildServerService; +import org.jetbrains.bsp.bazel.server.bsp.utils.InternalAspectsResolver; import org.jetbrains.bsp.bazel.server.loggers.BuildClientLogger; public class BazelBspServer { @@ -55,8 +60,12 @@ public void startServer(BspIntegrationData bspIntegrationData) { BazelBspCompilationManager bazelBspCompilationManager = new BazelBspCompilationManager(bazelRunner, bazelData); + InternalAspectsResolver internalAspectsResolver = + new InternalAspectsResolver( + bazelData.getBspProjectRoot(), Paths.get(bazelData.getWorkspaceRoot())); BazelBspAspectsManager bazelBspAspectsManager = - new BazelBspAspectsManager(bazelBspCompilationManager, bazelRunner); + new BazelBspAspectsManager( + bazelBspCompilationManager, bazelRunner, internalAspectsResolver); BazelCppTargetManager bazelCppTargetManager = new BazelCppTargetManager(bazelBspAspectsManager); BazelBspTargetManager bazelBspTargetManager = new BazelBspTargetManager(bazelRunner, bazelBspAspectsManager, bazelCppTargetManager); @@ -67,8 +76,6 @@ public void startServer(BspIntegrationData bspIntegrationData) { this.serverBuildManager = new BazelBspServerBuildManager( serverRequestHelpers, - bazelData, - bazelRunner, bazelBspCompilationManager, bazelBspAspectsManager, bazelBspTargetManager, @@ -84,14 +91,19 @@ public void startServer(BspIntegrationData bspIntegrationData) { bazelRunner, bazelBspServerConfig.getProjectView()); + JvmBuildServerService jvmBuildServerService = + new JvmBuildServerService(bazelData, bazelRunner, bazelBspAspectsManager); ScalaBuildServerService scalaBuildServerService = new ScalaBuildServerService( bazelData, bazelRunner, bazelBspCompilationManager, bazelBspQueryManager); JavaBuildServerService javaBuildServerService = new JavaBuildServerService( bazelBspCompilationManager, bazelBspQueryManager, bazelData, bazelRunner); - CppBuildServerService cppBuildServerService = new CppBuildServerService(bazelRunner); + CppBuildServerService cppBuildServerService = + new CppBuildServerService(bazelRunner, bazelBspAspectsManager); + JvmBuildServer jvmBuildServer = + new JvmBuildServerImpl(jvmBuildServerService, serverRequestHelpers); ScalaBuildServer scalaBuildServer = new ScalaBuildServerImpl(scalaBuildServerService, serverRequestHelpers); JavaBuildServer javaBuildServer = @@ -101,7 +113,8 @@ public void startServer(BspIntegrationData bspIntegrationData) { BuildServer buildServer = new BuildServerImpl(buildServerService, serverRequestHelpers); this.bspImplementationHub = - new BspImplementationHub(buildServer, scalaBuildServer, javaBuildServer, cppBuildServer); + new BspImplementationHub( + buildServer, jvmBuildServer, scalaBuildServer, javaBuildServer, cppBuildServer); integrateBsp(bspIntegrationData); } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/ServerInitializer.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/ServerInitializer.java index 74a09f9f6..17b42b794 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/ServerInitializer.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/ServerInitializer.java @@ -33,16 +33,17 @@ public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); try { - Path rootDir = Paths.get(Constants.BAZELBSP_DIR_NAME).toAbsolutePath(); + var bspProjectRoot = Paths.get("").toAbsolutePath(); + var rootDir = bspProjectRoot.resolve(Constants.BAZELBSP_DIR_NAME); Files.createDirectories(rootDir); - Path traceFile = rootDir.resolve(Constants.BAZELBSP_TRACE_JSON_FILE_NAME); + var traceFile = rootDir.resolve(Constants.BAZELBSP_TRACE_JSON_FILE_NAME); PrintWriter traceWriter = new PrintWriter( Files.newOutputStream( traceFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)); - BazelBspServerConfig bazelBspServerConfig = getBazelBspServerConfig(args); + var bazelBspServerConfig = getBazelBspServerConfig(bspProjectRoot, args); BspIntegrationData bspIntegrationData = new BspIntegrationData(stdout, stdin, executor, traceWriter); @@ -66,23 +67,26 @@ public static void main(String[] args) { } } - private static BazelBspServerConfig getBazelBspServerConfig(String[] args) { - String pathToBazel = args[0]; - ProjectView projectView = getProjectView(args); + private static BazelBspServerConfig getBazelBspServerConfig(Path bspProjectRoot, String[] args) { + var projectView = getProjectView(bspProjectRoot, args); + var pathToBazel = projectView.getBazelPath().get().getValue(); return new BazelBspServerConfig(pathToBazel, projectView); } - private static ProjectView getProjectView(String[] args) { - ProjectViewProvider provider = getProjectViewProvider(args); + private static ProjectView getProjectView(Path bspProjectRoot, String[] args) { + ProjectViewProvider provider = getProjectViewProvider(bspProjectRoot, args); - return provider.create(); + return provider.create().get(); } - private static ProjectViewProvider getProjectViewProvider(String[] args) { + private static ProjectViewProvider getProjectViewProvider(Path bspProjectRoot, String[] args) { + if (args.length == 1) { + return new ServerArgsProjectViewProvider(bspProjectRoot, args[0]); + } if (args.length == 2) { - return new ServerArgsProjectViewProvider(args[1]); + return new ServerArgsProjectViewProvider(bspProjectRoot, args[0], args[1]); } - return new ProjectViewDefaultParserProvider(); + return new ProjectViewDefaultParserProvider(bspProjectRoot); } } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/BepServer.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/BepServer.java index 2733cfa3a..8c244072c 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/BepServer.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/BepServer.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.bsp.bazel.bazelrunner.data.BazelData; @@ -36,6 +37,7 @@ import org.jetbrains.bsp.bazel.commons.ExitCodeMapper; import org.jetbrains.bsp.bazel.commons.Uri; import org.jetbrains.bsp.bazel.server.bep.parsers.ClasspathParser; +import org.jetbrains.bsp.bazel.server.bep.parsers.error.FileDiagnostic; import org.jetbrains.bsp.bazel.server.bep.parsers.error.StderrDiagnosticsParser; import org.jetbrains.bsp.bazel.server.loggers.BuildClientLogger; @@ -47,6 +49,10 @@ public class BepServer extends PublishBuildEventGrpc.PublishBuildEventImplBase { ImmutableList.of("Loading: 0 packages loaded"); private static final int URI_PREFIX_LENGTH = 7; + public static final Set EXPECTED_OUTPUT_GROUPS = + Set.of( + Constants.SCALA_COMPILER_CLASSPATH_FILES, + Constants.JAVA_RUNTIME_CLASSPATH_ASPECT_OUTPUT_GROUP); private final BuildClient bspClient; private final BuildClientLogger buildClientLogger; @@ -168,13 +174,13 @@ private void consumeCompletedEvent(BuildEventStreamProtos.TargetComplete targetC LOGGER.info("Consuming target completed event " + targetComplete); if (outputGroups.size() == 1) { OutputGroup outputGroup = outputGroups.get(0); - if (outputGroup.getName().equals(Constants.SCALA_COMPILER_CLASSPATH_FILES)) { - fetchScalaJars(outputGroup); + if (EXPECTED_OUTPUT_GROUPS.contains(outputGroup.getName())) { + fetchOutputGroup(outputGroup); } } } - private void fetchScalaJars(OutputGroup outputGroup) { + private void fetchOutputGroup(OutputGroup outputGroup) { outputGroup.getFileSetsList().stream() .flatMap(fileSetId -> namedSetsOfFiles.get(fileSetId.getId()).getFilesList().stream()) .map(file -> URI.create(file.getUri())) @@ -254,10 +260,7 @@ private void processProgressEvent(BuildEventStreamProtos.BuildEvent event) { } private void consumeProgressEvent(BuildEventStreamProtos.Progress progress) { - Map> fileDiagnostics = - StderrDiagnosticsParser.parse(progress.getStderr()); - - fileDiagnostics.entrySet().stream() + StderrDiagnosticsParser.parse(progress.getStderr()).entrySet().stream() .map(this::createParamsFromEntry) .forEach(bspClient::onBuildPublishDiagnostics); @@ -265,15 +268,26 @@ private void consumeProgressEvent(BuildEventStreamProtos.Progress progress) { } private PublishDiagnosticsParams createParamsFromEntry( - Map.Entry> entry) { - String fileLocation = entry.getKey(); - List diagnostics = entry.getValue(); - - return new PublishDiagnosticsParams( - new TextDocumentIdentifier(Uri.fromAbsolutePath(fileLocation).toString()), - new BuildTargetIdentifier(""), - diagnostics, - false); + Map.Entry> entry) { + var rawFileLocation = entry.getKey(); + var fileLocation = new TextDocumentIdentifier(Uri.fromAbsolutePath(rawFileLocation).toString()); + + var fileDiagnostics = entry.getValue(); + var targetId = getTargetIdFromDiagnostics(fileDiagnostics); + var diagnostics = getDiagnosticsFromFileDiagnostics(fileDiagnostics); + + return new PublishDiagnosticsParams(fileLocation, targetId, diagnostics, false); + } + + private BuildTargetIdentifier getTargetIdFromDiagnostics(List diagnostics) { + return diagnostics.stream() + .findFirst() + .map(FileDiagnostic::getTarget) + .orElse(new BuildTargetIdentifier("")); + } + + private List getDiagnosticsFromFileDiagnostics(List diagnostics) { + return diagnostics.stream().map(FileDiagnostic::getDiagnostic).collect(Collectors.toList()); } private void logProgress(BuildEventStreamProtos.Progress progress) { diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD index 075c72628..70beac725 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD @@ -3,7 +3,7 @@ load("@rules_java//java:defs.bzl", "java_library") java_library( name = "error", srcs = glob(["*.java"]), - visibility = ["//server/src/main/java/org/jetbrains/bsp/bazel/server/bep:__subpackages__"], + visibility = ["//server:__subpackages__"], deps = [ "//commons", "@maven//:ch_epfl_scala_bsp4j", diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnostic.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnostic.java index 55c8924e1..78d25e16a 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnostic.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnostic.java @@ -1,49 +1,106 @@ package org.jetbrains.bsp.bazel.server.bep.parsers.error; +import ch.epfl.scala.bsp4j.BuildTargetIdentifier; import ch.epfl.scala.bsp4j.Diagnostic; import ch.epfl.scala.bsp4j.DiagnosticSeverity; import ch.epfl.scala.bsp4j.Position; import ch.epfl.scala.bsp4j.Range; +import java.util.ArrayList; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -class FileDiagnostic { +public class FileDiagnostic { private static final Logger LOGGER = LogManager.getLogger(FileDiagnostic.class); private final Diagnostic diagnostic; private final String fileLocation; + private final BuildTargetIdentifier target; - private FileDiagnostic(Diagnostic diagnostic, String fileLocation) { + FileDiagnostic(Diagnostic diagnostic, String fileLocation, BuildTargetIdentifier target) { this.diagnostic = diagnostic; this.fileLocation = fileLocation; + this.target = target; } - public static FileDiagnostic fromError(String error) { + public static Stream fromError(String error) { LOGGER.info("Error: {}", error); - String erroredFile = ErrorFileParser.getFileWithError(error); - String fileInfo = ErrorFileParser.extractFileInfo(error); - String fileLocation = getFileLocation(erroredFile, fileInfo); + var targetId = findTargetId(error); - return createFileDiagnostic(error, fileLocation); + return Stream.concat( + Stream.of(createBUILDFileDiagnostic(error, targetId)), + createSourceFilesDiagnostics(error, targetId)); } - private static FileDiagnostic createFileDiagnostic(String error, String fileLocation) { - Position position = ErrorPositionParser.getErrorPosition(error); - Range diagnosticRange = new Range(position, position); - Diagnostic diagnostic = new Diagnostic(diagnosticRange, error); + private static BuildTargetIdentifier findTargetId(String error) { + var matcher = Pattern.compile("//[^ :]*:?[^ :]+").matcher(error); + + if (matcher.find()) { + var rawTargetId = matcher.group(); + return new BuildTargetIdentifier(rawTargetId); + } + + return new BuildTargetIdentifier(""); + } + + private static FileDiagnostic createBUILDFileDiagnostic( + String error, BuildTargetIdentifier targetId) { + var erroredFile = ErrorFileParser.getFileWithError(error); + var fileInfo = ErrorFileParser.extractFileInfo(error); + var fileLocation = getFileLocation(erroredFile, fileInfo); + var message = error.split("\n", 2)[0]; + + return createFileDiagnostic(message, fileLocation, targetId); + } + + private static FileDiagnostic createFileDiagnostic( + String error, String fileLocation, BuildTargetIdentifier targetId) { + var position = ErrorPositionParser.getErrorPosition(error); + var diagnosticRange = new Range(position, position); + var diagnostic = new Diagnostic(diagnosticRange, error); diagnostic.setSeverity(DiagnosticSeverity.ERROR); - return new FileDiagnostic(diagnostic, fileLocation); + return new FileDiagnostic(diagnostic, fileLocation, targetId); } private static String getFileLocation(String erroredFile, String fileInfo) { - int fileLocationUrlEndIndex = fileInfo.indexOf(erroredFile) + erroredFile.length(); + var fileLocationUrlEndIndex = fileInfo.indexOf(erroredFile) + erroredFile.length(); return fileInfo.substring(0, fileLocationUrlEndIndex); } + private static Stream createSourceFilesDiagnostics( + String error, BuildTargetIdentifier targetId) { + var pattern = Pattern.compile("\n((([^\\s\\/:]+\\/)*[^\\/:]+):(\\d+):[^\\^]*\\^)"); + var matcher = pattern.matcher(error); + + var result = new ArrayList(); + + while (matcher.find()) { + var message = matcher.group(1); + var filePath = matcher.group(2); + var errorLine = matcher.group(4); + + result.add(createSourceFileDiagnostic(message, filePath, errorLine, targetId)); + } + + return result.stream(); + } + + private static FileDiagnostic createSourceFileDiagnostic( + String message, String filePath, String errorLine, BuildTargetIdentifier targetId) { + var position = new Position(Integer.parseInt(errorLine), 0); + var diagnosticRange = new Range(position, position); + var diagnostic = new Diagnostic(diagnosticRange, message); + diagnostic.setSeverity(DiagnosticSeverity.ERROR); + + return new FileDiagnostic(diagnostic, filePath, targetId); + } + public Diagnostic getDiagnostic() { return diagnostic; } @@ -51,4 +108,39 @@ public Diagnostic getDiagnostic() { public String getFileLocation() { return fileLocation; } + + public BuildTargetIdentifier getTarget() { + return target; + } + + @Override + public String toString() { + return "FileDiagnostic{" + + "diagnostic=" + + diagnostic + + ", fileLocation='" + + fileLocation + + '\'' + + ", target=" + + target + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FileDiagnostic that = (FileDiagnostic) o; + // TODO normal .equals() fails don't know why... + return Objects.equals(diagnostic.getMessage(), that.diagnostic.getMessage()) + && Objects.equals(diagnostic.getRange(), that.diagnostic.getRange()) + && Objects.equals(diagnostic.getSeverity(), that.diagnostic.getSeverity()) + && Objects.equals(fileLocation, that.fileLocation) + && Objects.equals(target, that.target); + } + + @Override + public int hashCode() { + return Objects.hash(diagnostic, fileLocation, target); + } } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParser.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParser.java index 5292eeed9..37bb643c7 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParser.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParser.java @@ -1,30 +1,25 @@ package org.jetbrains.bsp.bazel.server.bep.parsers.error; -import ch.epfl.scala.bsp4j.Diagnostic; import com.google.common.base.Splitter; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; public final class StderrDiagnosticsParser { private static final String ERROR = "ERROR"; - private static final String STDERR_DELIMITER = "\n"; - - public static Map> parse(String stderr) { - return splitStderr(stderr).stream() + public static Map> parse(String stderr) { + return splitStderr(stderr) .filter(StderrDiagnosticsParser::isError) .filter(StderrDiagnosticsParser::isBazelError) - .map(FileDiagnostic::fromError) - .collect( - Collectors.groupingBy( - FileDiagnostic::getFileLocation, - Collectors.mapping(FileDiagnostic::getDiagnostic, Collectors.toList()))); + .flatMap(FileDiagnostic::fromError) + .collect(Collectors.groupingBy(FileDiagnostic::getFileLocation, Collectors.toList())); } - private static List splitStderr(String stderr) { - return Splitter.on(STDERR_DELIMITER).splitToList(stderr); + private static Stream splitStderr(String stderr) { + return Splitter.onPattern("(?=(ERROR:))").splitToList(stderr).stream(); } private static boolean isError(String stderrPart) { diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BazelBspServerBuildManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BazelBspServerBuildManager.java index 8e69a0047..3e2fce0c9 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BazelBspServerBuildManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BazelBspServerBuildManager.java @@ -12,8 +12,6 @@ import java.util.stream.Collectors; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; -import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; -import org.jetbrains.bsp.bazel.bazelrunner.data.BazelData; import org.jetbrains.bsp.bazel.commons.Constants; import org.jetbrains.bsp.bazel.commons.Lazy; import org.jetbrains.bsp.bazel.server.bep.BepServer; @@ -25,10 +23,9 @@ public class BazelBspServerBuildManager { - public static final String BAZEL_PRINT_ASPECT = "@//.bazelbsp:aspects.bzl%print_aspect"; + public static final String BAZEL_PRINT_ASPECT = "print_aspect"; private final BazelBspServerRequestHelpers serverRequestHelpers; - private final BazelData bazelData; private final BazelBspQueryManager bazelBspQueryManager; private final BazelBspCompilationManager bazelBspCompilationManager; private final BazelBspTargetManager bazelBspTargetManager; @@ -39,15 +36,12 @@ public class BazelBspServerBuildManager { public BazelBspServerBuildManager( BazelBspServerRequestHelpers serverRequestHelpers, - BazelData bazelData, - BazelRunner bazelRunner, BazelBspCompilationManager bazelBspCompilationManager, BazelBspAspectsManager bazelBspAspectsManager, BazelBspTargetManager bazelBspTargetManager, BazelCppTargetManager bazelCppTargetManager, BazelBspQueryManager bazelBspQueryManager) { this.serverRequestHelpers = serverRequestHelpers; - this.bazelData = bazelData; this.bazelBspCompilationManager = bazelBspCompilationManager; this.bazelBspAspectsManager = bazelBspAspectsManager; this.bazelCppTargetManager = bazelCppTargetManager; @@ -64,27 +58,13 @@ public List getSourceItems(Build.Rule rule, BuildTargetIdentifier la return bazelBspQueryManager.getSourceItems(rule, label); } - public String getSourcesRoot(String uri) { - List root = - Constants.KNOWN_SOURCE_ROOTS.stream().filter(uri::contains).collect(Collectors.toList()); - return bazelData.getWorkspaceRoot() - + (root.size() == 0 - ? "" - : uri.substring(1, uri.indexOf(root.get(0)) + root.get(0).length())); - } - public List lookUpTransitiveSourceJars(String target) { // TODO(illicitonion): Use an aspect output group, rather than parsing stderr // logging return bazelBspAspectsManager .fetchLinesFromAspect(target, BAZEL_PRINT_ASPECT) - .filter( - parts -> - parts.size() == 3 - && parts.get(0).equals(BazelBspAspectsManager.DEBUG_MESSAGE) - && parts.get(1).contains(BazelBspAspectsManager.ASPECT_LOCATION) - && parts.get(2).endsWith(".jar")) - .map(parts -> Constants.EXEC_ROOT_PREFIX + parts.get(2)) + .filter(path -> path.endsWith(".jar")) + .map(path -> Constants.EXEC_ROOT_PREFIX + path) .collect(Collectors.toList()); } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BspImplementationHub.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BspImplementationHub.java index e929c7adf..9927f9d2b 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BspImplementationHub.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/BspImplementationHub.java @@ -19,6 +19,11 @@ import ch.epfl.scala.bsp4j.JavaBuildServer; import ch.epfl.scala.bsp4j.JavacOptionsParams; import ch.epfl.scala.bsp4j.JavacOptionsResult; +import ch.epfl.scala.bsp4j.JvmBuildServer; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentResult; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentResult; import ch.epfl.scala.bsp4j.ResourcesParams; import ch.epfl.scala.bsp4j.ResourcesResult; import ch.epfl.scala.bsp4j.RunParams; @@ -38,19 +43,22 @@ import java.util.concurrent.CompletableFuture; public class BspImplementationHub - implements BuildServer, ScalaBuildServer, JavaBuildServer, CppBuildServer { + implements BuildServer, JvmBuildServer, ScalaBuildServer, JavaBuildServer, CppBuildServer { private final BuildServer buildServer; + private final JvmBuildServer jvmBuildServer; private final ScalaBuildServer scalaBuildServer; private final JavaBuildServer javaBuildServer; private final CppBuildServer cppBuildServer; public BspImplementationHub( BuildServer buildServer, + JvmBuildServer jvmBuildServer, ScalaBuildServer scalaBuildServer, JavaBuildServer javaBuildServer, CppBuildServer cppBuildServer) { this.buildServer = buildServer; + this.jvmBuildServer = jvmBuildServer; this.scalaBuildServer = scalaBuildServer; this.javaBuildServer = javaBuildServer; this.cppBuildServer = cppBuildServer; @@ -162,4 +170,16 @@ public CompletableFuture buildTargetCppOptions( CppOptionsParams cppOptionsParams) { return cppBuildServer.buildTargetCppOptions(cppOptionsParams); } + + @Override + public CompletableFuture jvmRunEnvironment( + JvmRunEnvironmentParams jvmRunEnvironmentParams) { + return jvmBuildServer.jvmRunEnvironment(jvmRunEnvironmentParams); + } + + @Override + public CompletableFuture jvmTestEnvironment( + JvmTestEnvironmentParams jvmTestEnvironmentParams) { + return jvmBuildServer.jvmTestEnvironment(jvmTestEnvironmentParams); + } } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/BUILD b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/BUILD index f244f8351..8e30a1aca 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/BUILD +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/BUILD @@ -5,7 +5,7 @@ java_library( srcs = glob(["*.java"]), visibility = ["//server:__subpackages__"], deps = [ - "//projectview:model", + "//projectview", "@maven//:com_google_guava_guava", ], ) diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/ServerArgsProjectViewProvider.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/ServerArgsProjectViewProvider.java index 5dff169b2..79e5bdc7b 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/ServerArgsProjectViewProvider.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/config/ServerArgsProjectViewProvider.java @@ -1,28 +1,47 @@ package org.jetbrains.bsp.bazel.server.bsp.config; import com.google.common.collect.ImmutableList; -import java.nio.file.Paths; +import io.vavr.control.Try; +import java.nio.file.Path; import java.util.Arrays; +import java.util.Optional; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; import org.jetbrains.bsp.bazel.projectview.model.ProjectViewProvider; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.DirectoriesSection; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewBazelPathSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; +import org.jetbrains.bsp.bazel.projectview.parser.ProjectViewDefaultParserProvider; +@Deprecated public class ServerArgsProjectViewProvider implements ProjectViewProvider { - private final String targets; + private final ProjectViewBazelPathSection pathToBazel; + private final Optional targets; - public ServerArgsProjectViewProvider(String targets) { - this.targets = targets; + private final ProjectViewDefaultParserProvider defaultParserProvider; + + public ServerArgsProjectViewProvider(Path bspProjectRoot, String pathToBazel, String targets) { + this.pathToBazel = new ProjectViewBazelPathSection(pathToBazel); + this.targets = + Optional.of( + new ProjectViewTargetsSection(Arrays.asList(targets.split(",")), ImmutableList.of())); + this.defaultParserProvider = new ProjectViewDefaultParserProvider(bspProjectRoot); + } + + public ServerArgsProjectViewProvider(Path bspProjectRoot, String pathToBazel) { + this.pathToBazel = new ProjectViewBazelPathSection(pathToBazel); + this.targets = Optional.empty(); + this.defaultParserProvider = new ProjectViewDefaultParserProvider(bspProjectRoot); } @Override - public ProjectView create() { - DirectoriesSection directoriesSection = - new DirectoriesSection(ImmutableList.of(Paths.get(".")), ImmutableList.of()); - TargetsSection targetsSection = - new TargetsSection(Arrays.asList(targets.split(",")), ImmutableList.of()); + public Try create() { + var parsedProjectView = defaultParserProvider.create().get(); - return ProjectView.builder().directories(directoriesSection).targets(targetsSection).build(); + return ProjectView.builder() + .targets(targets.orElse(parsedProjectView.getTargets())) + .bazelPath(Optional.of(pathToBazel)) + .debuggerAddress(parsedProjectView.getDebuggerAddress()) + .javaPath(parsedProjectView.getJavaPath()) + .build(); } } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/impl/JvmBuildServerImpl.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/impl/JvmBuildServerImpl.java new file mode 100644 index 000000000..7e32e9376 --- /dev/null +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/impl/JvmBuildServerImpl.java @@ -0,0 +1,37 @@ +package org.jetbrains.bsp.bazel.server.bsp.impl; + +import ch.epfl.scala.bsp4j.JvmBuildServer; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentResult; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentResult; +import java.util.concurrent.CompletableFuture; +import org.jetbrains.bsp.bazel.server.bsp.BazelBspServerRequestHelpers; +import org.jetbrains.bsp.bazel.server.bsp.services.JvmBuildServerService; + +public class JvmBuildServerImpl implements JvmBuildServer { + + private final JvmBuildServerService jvmBuildServerService; + private final BazelBspServerRequestHelpers serverRequestHelpers; + + public JvmBuildServerImpl( + JvmBuildServerService jvmBuildServerService, + BazelBspServerRequestHelpers serverRequestHelpers) { + this.jvmBuildServerService = jvmBuildServerService; + this.serverRequestHelpers = serverRequestHelpers; + } + + @Override + public CompletableFuture jvmRunEnvironment( + JvmRunEnvironmentParams params) { + return serverRequestHelpers.executeCommand( + "jvmRunEnvironment", () -> jvmBuildServerService.jvmRunEnvironment(params)); + } + + @Override + public CompletableFuture jvmTestEnvironment( + JvmTestEnvironmentParams params) { + return serverRequestHelpers.executeCommand( + "jvmTestEnvironment", () -> jvmBuildServerService.jvmTestEnvironment(params)); + } +} diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspAspectsManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspAspectsManager.java index c23eae12d..4e4320df1 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspAspectsManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspAspectsManager.java @@ -9,50 +9,67 @@ import java.util.stream.Stream; import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; import org.jetbrains.bsp.bazel.bazelrunner.params.BazelRunnerFlag; -import org.jetbrains.bsp.bazel.commons.Constants; import org.jetbrains.bsp.bazel.commons.Uri; import org.jetbrains.bsp.bazel.server.bep.BepServer; +import org.jetbrains.bsp.bazel.server.bsp.utils.InternalAspectsResolver; public class BazelBspAspectsManager { public static final String DEBUG_MESSAGE = "DEBUG:"; - public static final String ASPECT_LOCATION = ".bazelbsp/aspects.bzl"; private final BazelBspCompilationManager bazelBspCompilationManager; private final BazelRunner bazelRunner; + private final InternalAspectsResolver aspectsResolver; private BepServer bepServer; public BazelBspAspectsManager( - BazelBspCompilationManager bazelBspCompilationManager, BazelRunner bazelRunner) { + BazelBspCompilationManager bazelBspCompilationManager, + BazelRunner bazelRunner, + InternalAspectsResolver aspectResolver) { this.bazelBspCompilationManager = bazelBspCompilationManager; this.bazelRunner = bazelRunner; + this.aspectsResolver = aspectResolver; } public List fetchPathsFromOutputGroup( List targets, String aspect, String outputGroup) { - String aspectFlag = String.format("--aspects=%s", aspect); + String aspectFlag = String.format("--aspects=%s", aspectsResolver.resolveLabel(aspect)); String outputGroupFlag = String.format("--output_groups=%s", outputGroup); bazelBspCompilationManager.buildTargetsWithBep( targets, ImmutableList.of(aspectFlag, outputGroupFlag)); return bepServer .getOutputGroupPaths() - .getOrDefault(Constants.SCALA_COMPILER_CLASSPATH_FILES, Collections.emptySet()) + .getOrDefault(outputGroup, Collections.emptySet()) .stream() .map(Uri::toString) .collect(Collectors.toList()); } - public Stream> fetchLinesFromAspect(String target, String aspect) { - List lines = + public Stream fetchLinesFromAspect(String target, String aspect) { + return fetchLinesFromAspect(target, aspect, false); + } + + public Stream fetchLinesFromAspect(String target, String aspect, boolean build) { + var builder = bazelRunner .commandBuilder() .build() - .withFlag(BazelRunnerFlag.NOBUILD) - .withFlag(BazelRunnerFlag.ASPECTS, aspect) - .withArgument(target) - .executeBazelCommand() - .getStderr(); + .withFlag(BazelRunnerFlag.ASPECTS, aspectsResolver.resolveLabel(aspect)) + .withArgument(target); + + if (!build) { + builder.withFlag(BazelRunnerFlag.NOBUILD); + } + + List lines = builder.executeBazelCommand().getStderr(); - return lines.stream().map(line -> Splitter.on(" ").splitToList(line)); + return lines.stream() + .map(line -> Splitter.on(" ").splitToList(line)) + .filter( + parts -> + parts.size() == 3 + && parts.get(0).equals(DEBUG_MESSAGE) + && parts.get(1).contains(aspectsResolver.getAspectOutputIndicator())) + .map(parts -> parts.get(2)); } public void setBepServer(BepServer bepServer) { diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspJvmTargetManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspJvmTargetManager.java index f57e8532d..d6b3fbb66 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspJvmTargetManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspJvmTargetManager.java @@ -14,8 +14,7 @@ import org.jetbrains.bsp.bazel.server.bsp.resolvers.QueryResolver; public class BazelBspJvmTargetManager extends Lazy { - public static final String FETCH_JAVA_VERSION_ASPECT = - "@//.bazelbsp:aspects.bzl%fetch_java_target_version"; + public static final String FETCH_JAVA_VERSION_ASPECT = "fetch_java_target_version"; public static final String BAZEL_JDK_CURRENT_JAVA_TOOLCHAIN = "@bazel_tools//tools/jdk:current_java_toolchain"; private final BazelRunner bazelRunner; @@ -82,13 +81,7 @@ private Optional getTargetFromAttribute(Build.Attribute attribute) private Optional getJavaVersion() { return bazelBspAspectsManager .fetchLinesFromAspect(BAZEL_JDK_CURRENT_JAVA_TOOLCHAIN, FETCH_JAVA_VERSION_ASPECT) - .filter( - parts -> - parts.size() == 3 - && parts.get(0).equals(BazelBspAspectsManager.DEBUG_MESSAGE) - && parts.get(1).contains(BazelBspAspectsManager.ASPECT_LOCATION) - && parts.get(2).chars().allMatch(Character::isDigit)) - .map(parts -> parts.get(2)) + .filter(line -> line.chars().allMatch(Character::isDigit)) .findFirst(); } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspQueryManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspQueryManager.java index 676c942b7..cbd4835ea 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspQueryManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspQueryManager.java @@ -5,6 +5,7 @@ import ch.epfl.scala.bsp4j.BuildTargetIdentifier; import ch.epfl.scala.bsp4j.SourceItem; import ch.epfl.scala.bsp4j.SourceItemKind; +import ch.epfl.scala.bsp4j.StatusCode; import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -58,7 +59,7 @@ public BazelBspQueryManager( } public Either getWorkspaceBuildTargets() { - List projectInitTargets = projectView.getTargets().getIncludedTargets(); + List projectInitTargets = projectView.getTargets().getIncludedValues(); List targets = projectInitTargets.stream() @@ -66,9 +67,27 @@ public Either getWorkspaceBuildTarge .flatMap(Collection::stream) .collect(Collectors.toList()); + buildProject(targets); + return Either.forRight(new WorkspaceBuildTargetsResult(targets)); } + private void buildProject(List buildTargets) { + var targets = buildTargets.stream().map(t -> t.getId().getUri()).collect(Collectors.toList()); + + var bazelProcessResult = + bazelRunner + .commandBuilder() + .build() + .withArguments(targets) + .executeBazelBesCommand() + .waitAndGetResult(); + + if (bazelProcessResult.getStatusCode() != StatusCode.OK) { + throw new RuntimeException("Failed to build the project"); + } + } + private List getBuildTargetForProjectPath(String target) { String targetWithExcludedTargets = TargetsUtils.getTargetWithExcludedTargets(projectView, target); diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspScalaTargetManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspScalaTargetManager.java index bea1ea489..b253552e3 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspScalaTargetManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelBspScalaTargetManager.java @@ -21,8 +21,7 @@ public class BazelBspScalaTargetManager extends Lazy { "@io_bazel_rules_scala_scala_reflect//:io_bazel_rules_scala_scala_reflect"; public static final String SCALA_COMPILER = "@io_bazel_rules_scala_scala_compiler//:io_bazel_rules_scala_scala_compiler"; - public static final String SCALA_COMPILER_ASPECT = - "@//.bazelbsp:aspects.bzl%scala_compiler_classpath_aspect"; + public static final String SCALA_COMPILER_ASPECT = "scala_compiler_classpath_aspect"; public static final String SCALA_COMPILER_OUTPUT_GROUP = "scala_compiler_classpath_files"; private final BazelBspAspectsManager bazelBspAspectsManager; diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelCppTargetManager.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelCppTargetManager.java index 7fe4b134f..203fa695e 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelCppTargetManager.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/managers/BazelCppTargetManager.java @@ -9,7 +9,7 @@ public class BazelCppTargetManager extends Lazy { private static final String BAZEL_CPP_TOOLCHAIN = "@bazel_tools//tools/cpp:toolchain"; - private static final String FETCH_CPP_ASPECT = "@//.bazelbsp:aspects.bzl%fetch_cpp_compiler"; + private static final String FETCH_CPP_ASPECT = "fetch_cpp_compiler"; private final BazelBspAspectsManager bazelBspAspectsManager; public BazelCppTargetManager(BazelBspAspectsManager bazelBspAspectsManager) { @@ -20,12 +20,6 @@ private Optional getCppBuildTarget() { List cppInfo = bazelBspAspectsManager .fetchLinesFromAspect(BAZEL_CPP_TOOLCHAIN, FETCH_CPP_ASPECT) - .filter( - parts -> - parts.size() == 3 - && parts.get(0).equals(BazelBspAspectsManager.DEBUG_MESSAGE) - && parts.get(1).contains(BazelBspAspectsManager.ASPECT_LOCATION)) - .map(parts -> parts.get(2)) .limit(2) .collect(Collectors.toList()); diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsLanguageOptionsResolver.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsLanguageOptionsResolver.java index 1729b95ee..df5fcc283 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsLanguageOptionsResolver.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsLanguageOptionsResolver.java @@ -3,10 +3,11 @@ import ch.epfl.scala.bsp4j.BuildTargetIdentifier; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.query2.proto.proto2api.Build; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.jetbrains.bsp.bazel.bazelrunner.BazelProcess; import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; import org.jetbrains.bsp.bazel.bazelrunner.data.BazelData; @@ -52,11 +53,11 @@ public List getResultItemsForTargets(List buildTargets actionGraphResolver.getActionGraphParser(targets, languagesIds); return targets.stream() - .flatMap(target -> getResultItems(target, targets, actionGraphParser)) + .flatMap(target -> getResultItems(target, targets, actionGraphParser).stream()) .collect(Collectors.toList()); } - private Stream getResultItems( + private Optional getResultItems( String target, List allTargets, ActionGraphParser actionGraphParser) { Map> targetsOptions = getTargetsOptions(allTargets); @@ -87,7 +88,7 @@ private List collectRules(Build.Rule rule) { return BuildRuleAttributeExtractor.extract(rule, compilerOptionsName); } - private Stream getResultItemForActionGraphParserOptionsTargetsOptionsAndTarget( + private Optional getResultItemForActionGraphParserOptionsTargetsOptionsAndTarget( ActionGraphParser actionGraphParser, Map> targetsOptions, String target) { @@ -97,6 +98,7 @@ private Stream getResultItemForActionGraphParserOptionsTargetsOptionsAndTarge List inputs = actionGraphParser.getInputsAsUri(target, bazelData.getExecRoot()); return actionGraphParser.getOutputs(target, ACTION_GRAPH_SUFFIXES).stream() + .max(Comparator.naturalOrder()) .map(this::mapActionGraphOutputsToClassDirectory) .map( classDirectory -> diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsUtils.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsUtils.java index 4655bd875..921d95734 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsUtils.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/resolvers/TargetsUtils.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.stream.Collectors; import org.jetbrains.bsp.bazel.projectview.model.ProjectView; -import org.jetbrains.bsp.bazel.projectview.model.sections.specific.TargetsSection; +import org.jetbrains.bsp.bazel.projectview.model.sections.ProjectViewTargetsSection; public final class TargetsUtils { @@ -25,16 +25,16 @@ public static String getKindInput(ProjectView projectView, String fileUri, Strin } public static String getAllProjectTargetsWithExcludedTargets(ProjectView projectView) { - TargetsSection targetsSection = projectView.getTargets(); - String excludedTargets = getExcludedTargets(targetsSection.getExcludedTargets()); - String includedTargets = Joiner.on(" ").join(targetsSection.getIncludedTargets()); + ProjectViewTargetsSection targetsSection = projectView.getTargets(); + String excludedTargets = getExcludedTargets(targetsSection.getExcludedValues()); + String includedTargets = Joiner.on(" ").join(targetsSection.getIncludedValues()); return String.format("%s %s", includedTargets, excludedTargets); } public static String getTargetWithExcludedTargets(ProjectView projectView, String target) { - TargetsSection targetsSection = projectView.getTargets(); - String excludedTargets = getExcludedTargets(targetsSection.getExcludedTargets()); + ProjectViewTargetsSection targetsSection = projectView.getTargets(); + String excludedTargets = getExcludedTargets(targetsSection.getExcludedValues()); return String.format("%s %s", target, excludedTargets); } diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/BuildServerService.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/BuildServerService.java index 282455b51..3c47fb703 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/BuildServerService.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/BuildServerService.java @@ -29,9 +29,9 @@ import ch.epfl.scala.bsp4j.TestProvider; import ch.epfl.scala.bsp4j.TestResult; import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.devtools.build.lib.query2.proto.proto2api.Build; +import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -58,6 +58,7 @@ import org.jetbrains.bsp.bazel.server.bsp.resolvers.QueryResolver; import org.jetbrains.bsp.bazel.server.bsp.resolvers.TargetRulesResolver; import org.jetbrains.bsp.bazel.server.bsp.resolvers.TargetsUtils; +import org.jetbrains.bsp.bazel.server.bsp.utils.SourceRootGuesser; public class BuildServerService { @@ -113,6 +114,8 @@ private Either handleBuildInitialize() { capabilities.setDependencySourcesProvider(true); capabilities.setInverseSourcesProvider(true); capabilities.setResourcesProvider(true); + capabilities.setJvmRunEnvironmentProvider(true); + capabilities.setJvmTestEnvironmentProvider(true); return Either.forRight( new InitializeBuildResult( Constants.NAME, Constants.VERSION, Constants.BSP_VERSION, capabilities)); @@ -161,31 +164,49 @@ public CompletableFuture workspaceBuildTargets() { public Either buildTargetSources(SourcesParams sourcesParams) { LOGGER.info("buildTargetSources call with param: {}", sourcesParams); + try { + TargetRulesResolver targetRulesResolver = + TargetRulesResolver.withBazelRunnerAndMapper( + bazelRunner, this::mapBuildRuleToSourcesItem); - TargetRulesResolver targetRulesResolver = - TargetRulesResolver.withBazelRunnerAndMapper(bazelRunner, this::mapBuildRuleToSourcesItem); - - List sourceItems = - targetRulesResolver.getItemsForTargets(sourcesParams.getTargets()); + List sourceItems = + targetRulesResolver.getItemsForTargets(sourcesParams.getTargets()); - SourcesResult sourcesResult = new SourcesResult(sourceItems); + SourcesResult sourcesResult = new SourcesResult(sourceItems); - return Either.forRight(sourcesResult); + return Either.forRight(sourcesResult); + } catch (Exception e) { + ResponseError fail = + new ResponseError(ResponseErrorCode.InternalError, e.getMessage(), e.getStackTrace()); + return Either.forLeft(fail); + } } private SourcesItem mapBuildRuleToSourcesItem(Build.Rule rule) { BuildTargetIdentifier ruleLabel = new BuildTargetIdentifier(rule.getName()); List items = serverBuildManager.getSourceItems(rule, ruleLabel); - List roots = getRuleRoots(rule); + List roots = getRuleRoots(items); return createSourcesForLabelAndItemsAndRoots(ruleLabel, items, roots); } - private List getRuleRoots(Build.Rule rule) { - String sourcesRootUriString = serverBuildManager.getSourcesRoot(rule.getName()); - Uri uri = Uri.fromAbsolutePath(sourcesRootUriString); - - return ImmutableList.of(uri.toString()); + private List getRuleRoots(List items) { + return items.stream() + .map(SourceItem::getUri) + .map( + uri -> { + try { + URL url = new URL(uri); + return url.toURI(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .map(SourceRootGuesser::getSourcesRoot) + .map(Uri::fromAbsolutePath) + .map(Uri::toString) + .distinct() + .collect(Collectors.toList()); } private SourcesItem createSourcesForLabelAndItemsAndRoots( @@ -364,8 +385,6 @@ public Either buildTargetRun(RunParams runParams) { public Either buildTargetCleanCache( CleanCacheParams cleanCacheParams) { LOGGER.info("buildTargetCleanCache call with param: {}", cleanCacheParams); - - CleanCacheResult result; try { List lines = bazelRunner @@ -375,13 +394,12 @@ public Either buildTargetCleanCache( .waitAndGetResult() .getStdout(); - result = new CleanCacheResult(String.join("\n", lines), true); + CleanCacheResult result = new CleanCacheResult(String.join("\n", lines), true); + return Either.forRight(result); } catch (RuntimeException e) { - // TODO does it make sense to return a successful response here? - // If we caught an exception here, there was an internal server error... - result = new CleanCacheResult(e.getMessage(), false); + ResponseError fail = new ResponseError(ResponseErrorCode.InternalError, e.getMessage(), null); + return Either.forLeft(fail); } - return Either.forRight(result); } public Either workspaceReload() { diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/CppBuildServerService.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/CppBuildServerService.java index c41bd23bc..5d61fe77b 100644 --- a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/CppBuildServerService.java +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/CppBuildServerService.java @@ -4,7 +4,6 @@ import ch.epfl.scala.bsp4j.CppOptionsItem; import ch.epfl.scala.bsp4j.CppOptionsParams; import ch.epfl.scala.bsp4j.CppOptionsResult; -import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.List; @@ -12,7 +11,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; -import org.jetbrains.bsp.bazel.bazelrunner.params.BazelRunnerFlag; import org.jetbrains.bsp.bazel.server.bsp.managers.BazelBspAspectsManager; public class CppBuildServerService { @@ -21,11 +19,13 @@ public class CppBuildServerService { public static final int LINKOPTS_LOCATION = 2; public static final int LINKSHARED_LOCATION = 3; private final BazelRunner bazelRunner; - private static final String FETCH_CPP_TARGET_ASPECT = - "@//.bazelbsp:aspects.bzl%get_cpp_target_info"; + private static final String FETCH_CPP_TARGET_ASPECT = "get_cpp_target_info"; + private BazelBspAspectsManager bazelBspAspectsManager; - public CppBuildServerService(BazelRunner bazelRunner) { + public CppBuildServerService( + BazelRunner bazelRunner, BazelBspAspectsManager bazelBspAspectsManager) { this.bazelRunner = bazelRunner; + this.bazelBspAspectsManager = bazelBspAspectsManager; } public Either buildTargetCppOptions( @@ -37,24 +37,9 @@ public Either buildTargetCppOptions( } private CppOptionsItem getOptions(BuildTargetIdentifier buildTargetIdentifier) { - List lines = - bazelRunner - .commandBuilder() - .build() - .withFlag(BazelRunnerFlag.ASPECTS, FETCH_CPP_TARGET_ASPECT) - .withArgument(buildTargetIdentifier.getUri()) - .executeBazelBesCommand() - .getStderr(); - List targetInfo = - lines.stream() - .map(line -> Splitter.on(" ").splitToList(line)) - .filter( - parts -> - parts.size() == 3 - && parts.get(0).equals(BazelBspAspectsManager.DEBUG_MESSAGE) - && parts.get(1).contains(BazelBspAspectsManager.ASPECT_LOCATION)) - .map(parts -> parts.get(2)) + bazelBspAspectsManager + .fetchLinesFromAspect(buildTargetIdentifier.getUri(), FETCH_CPP_TARGET_ASPECT, true) .collect(Collectors.toList()); if (targetInfo.size() != 4) { diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/JvmBuildServerService.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/JvmBuildServerService.java new file mode 100644 index 000000000..0c122142c --- /dev/null +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/services/JvmBuildServerService.java @@ -0,0 +1,85 @@ +package org.jetbrains.bsp.bazel.server.bsp.services; + +import ch.epfl.scala.bsp4j.BuildTargetIdentifier; +import ch.epfl.scala.bsp4j.JvmEnvironmentItem; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmRunEnvironmentResult; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentParams; +import ch.epfl.scala.bsp4j.JvmTestEnvironmentResult; +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.jsonrpc.messages.ResponseError; +import org.jetbrains.bsp.bazel.bazelrunner.BazelRunner; +import org.jetbrains.bsp.bazel.bazelrunner.data.BazelData; +import org.jetbrains.bsp.bazel.commons.Constants; +import org.jetbrains.bsp.bazel.server.bsp.managers.BazelBspAspectsManager; +import org.jetbrains.bsp.bazel.server.bsp.resolvers.TargetsLanguageOptionsResolver; + +public class JvmBuildServerService { + + private static final Logger LOGGER = LogManager.getLogger(JvmBuildServerService.class); + private static final ImmutableList JVM_LANGUAGES_IDS = + ImmutableList.of(Constants.JAVAC, Constants.KOTLINC, Constants.SCALAC); + public static final String JAVA_RUNTIME_CLASSPATH_ASPECT = "java_runtime_classpath_aspect"; + + private final TargetsLanguageOptionsResolver targetsLanguageOptionsResolver; + private final BazelBspAspectsManager bazelBspAspectsManager; + + public JvmBuildServerService( + BazelData bazelData, BazelRunner bazelRunner, BazelBspAspectsManager bazelBspAspectsManager) { + Map environmentVariables = System.getenv(); + String workspaceRoot = bazelData.getWorkspaceRoot(); + this.targetsLanguageOptionsResolver = + TargetsLanguageOptionsResolver.builder() + .bazelData(bazelData) + .bazelRunner(bazelRunner) + .compilerOptionsName(Constants.JVM_FLAGS_ATTR_NAME) + .languagesIds(JVM_LANGUAGES_IDS) + .resultItemsCollector( + (target, options, classpath, classDirectory) -> + new JvmEnvironmentItem( + target, + Collections.emptyList(), + options, + workspaceRoot, + environmentVariables)) + .build(); + this.bazelBspAspectsManager = bazelBspAspectsManager; + } + + public Either jvmRunEnvironment( + JvmRunEnvironmentParams params) { + LOGGER.info("jvmRunEnvironment call with param: {}", params); + List items = getJvmEnvironmentItems(params.getTargets()); + return Either.forRight(new JvmRunEnvironmentResult(items)); + } + + public Either jvmTestEnvironment( + JvmTestEnvironmentParams params) { + LOGGER.info("jvmTestEnvironment call with param: {}", params); + List items = getJvmEnvironmentItems(params.getTargets()); + return Either.forRight(new JvmTestEnvironmentResult(items)); + } + + private List getJvmEnvironmentItems(List targets) { + var items = + targetsLanguageOptionsResolver.getResultItemsForTargets(targets).stream() + .distinct() + .collect(Collectors.toList()); + items.forEach(item -> item.setClasspath(fetchClasspath(item.getTarget()))); + return items; + } + + private List fetchClasspath(BuildTargetIdentifier target) { + return bazelBspAspectsManager.fetchPathsFromOutputGroup( + List.of(target), + JAVA_RUNTIME_CLASSPATH_ASPECT, + Constants.JAVA_RUNTIME_CLASSPATH_ASPECT_OUTPUT_GROUP); + } +} diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolver.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolver.java new file mode 100644 index 000000000..3e55b74b0 --- /dev/null +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolver.java @@ -0,0 +1,21 @@ +package org.jetbrains.bsp.bazel.server.bsp.utils; + +import java.nio.file.Path; + +public class InternalAspectsResolver { + + private final String prefix; + + public InternalAspectsResolver(Path bspProjectRoot, Path workspaceRoot) { + var relative = workspaceRoot.relativize(bspProjectRoot).resolve(".bazelbsp").toString(); + prefix = String.format("@//%s:aspects.bzl%%", relative); + } + + public String getAspectOutputIndicator() { + return ".bazelbsp/aspects.bzl"; + } + + public String resolveLabel(String aspect) { + return prefix + aspect; + } +} diff --git a/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesser.java b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesser.java new file mode 100644 index 000000000..9fec432fe --- /dev/null +++ b/server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesser.java @@ -0,0 +1,45 @@ +package org.jetbrains.bsp.bazel.server.bsp.utils; + +import com.google.common.collect.ImmutableList; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.Optional; + +public class SourceRootGuesser { + + public static String getSourcesRoot(URI sourceUri) { + Path sourcePath = Paths.get(sourceUri); + FileSystem fs = FileSystems.getDefault(); + PathMatcher sourceRootPattern = + fs.getPathMatcher( + "glob:**/" + + "{main,test,tests,src,3rdparty,3rd_party,thirdparty,third_party}/" + + "{resources,scala,java,kotlin,jvm,proto,python,protobuf,py}"); + PathMatcher defaultTestRootPattern = fs.getPathMatcher("glob:**/{test,tests}"); + + Optional sourceRootGuess = + ImmutableList.of(sourceRootPattern, defaultTestRootPattern).stream() + .map(pattern -> approximateSourceRoot(sourcePath, pattern)) + .filter(Objects::nonNull) + .findFirst(); + + return sourceRootGuess.orElse(sourcePath.getParent()).toAbsolutePath().toString(); + } + + private static Path approximateSourceRoot(Path dir, PathMatcher matcher) { + Path guess = dir; + while (guess != null) { + if (matcher.matches(guess)) { + return guess; + } else { + guess = guess.getParent(); + } + } + return null; + } +} diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD new file mode 100644 index 000000000..7e81855c3 --- /dev/null +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/BUILD @@ -0,0 +1,29 @@ +load("@rules_java//java:defs.bzl", "java_test") + +java_test( + name = "FileDiagnosticTest", + size = "small", + srcs = ["FileDiagnosticTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error", + "@maven//:ch_epfl_scala_bsp4j", + "@maven//:com_google_guava_guava", + ], +) + +java_test( + name = "StderrDiagnosticsParserTest", + size = "small", + srcs = ["StderrDiagnosticsParserTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//server/src/main/java/org/jetbrains/bsp/bazel/server/bep/parsers/error", + "@maven//:ch_epfl_scala_bsp4j", + "@maven//:com_google_guava_guava", + ], +) diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnosticTest.java b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnosticTest.java new file mode 100644 index 000000000..cd586c9e6 --- /dev/null +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/FileDiagnosticTest.java @@ -0,0 +1,197 @@ +package org.jetbrains.bsp.bazel.server.bep.parsers.error; + +import static org.junit.Assert.assertEquals; + +import ch.epfl.scala.bsp4j.BuildTargetIdentifier; +import ch.epfl.scala.bsp4j.Diagnostic; +import ch.epfl.scala.bsp4j.DiagnosticSeverity; +import ch.epfl.scala.bsp4j.Position; +import ch.epfl.scala.bsp4j.Range; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class FileDiagnosticTest { + + private final String error; + private final List expectedDiagnostics; + + public FileDiagnosticTest(String error, List expectedDiagnostic) { + this.error = error; + this.expectedDiagnostics = expectedDiagnostic; + } + + @Parameters(name = "{index}: FileDiagnostic.fromError({0}) should equals {1}") + public static List data() { + return List.of( + new Object[][] { + { + // given - error in BUILD file + "ERROR: /user/workspace/path/to/package/BUILD:12:37: in java_test rule" + + " //path/to/package:test: target '//path/to/another/package:lib' is not visible" + + " from target '//path/to/package:test'. Check the visibility declaration of the" + + " former target if you think the dependency is legitimate", + // then + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: in java_test rule" + + " //path/to/package:test: target '//path/to/another/package:lib' is" + + " not visible from target '//path/to/package:test'. Check the" + + " visibility declaration of the former target if you think the" + + " dependency is legitimate") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test"))) + }, + { + // given - error in one source file + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala //path/to/package:test" + + " failed: (Exit 1): scalac failed: error executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params\n" + + "path/to/package/Test.scala:3: error: type mismatch;\n" + + " found : String(\"test\")\n" + + " required: Int\n" + + " val foo: Int = \"test\"\n" + + " ^\n" + + "one error found\n" + + "Build failed\n" + + "java.lang.RuntimeException: Build failed\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:280)\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:63)\n" + + "\tat io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:92)\n" + + "\tat io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:46)\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:26)\n" + + "Target //path/to/package:test failed to build\n" + + "Use --verbose_failures to see the command lines of failed build steps.\n" + + "INFO: Elapsed time: 0.220s, Critical Path: 0.09s\n" + + "INFO: 2 processes: 2 internal.\n" + + "FAILED: Build did NOT complete successfully", + // then + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala" + + " //path/to/package:test failed: (Exit 1): scalac failed: error" + + " executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test")), + new FileDiagnostic( + new Diagnostic( + new Range(new Position(3, 0), new Position(3, 0)), + "path/to/package/Test.scala:3: error: type mismatch;\n" + + " found : String(\"test\")\n" + + " required: Int\n" + + " val foo: Int = \"test\"\n" + + " ^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test.scala", + new BuildTargetIdentifier("//path/to/package:test"))) + }, + { + // given - errors in two source files + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala //path/to/package:test" + + " failed (Exit 1): scalac failed: error executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params\n" + + "path/to/package/Test1.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^\n" + + "path/to/package/Test2.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^\n" + + "2 errors\n" + + "Build failed\n" + + "java.lang.RuntimeException: Build failed\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:280)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:63)\n" + + "at io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:92)\n" + + "at io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:46)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:26)\n" + + "Target //path/to/package:test failed to build\n" + + "Use --verbose_failures to see the command lines of failed build steps.\n" + + "INFO: Elapsed time: 0.216s, Critical Path: 0.12s\n" + + "INFO: 2 processes: 2 internal.\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully", + // then + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala" + + " //path/to/package:test failed (Exit 1): scalac failed: error" + + " executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test")), + new FileDiagnostic( + new Diagnostic( + new Range(new Position(21, 0), new Position(21, 0)), + "path/to/package/Test1.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test1.scala", + new BuildTargetIdentifier("//path/to/package:test")), + new FileDiagnostic( + new Diagnostic( + new Range(new Position(37, 0), new Position(37, 0)), + "path/to/package/Test2.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test2.scala", + new BuildTargetIdentifier("//path/to/package:test"))) + } + }); + } + + @Test + public void shouldReturnDiagnosticForBUILDFileAndSources() { + // when + var diagnostics = FileDiagnostic.fromError(error).collect(Collectors.toList()); + + // then + assertEquals(expectedDiagnostics, diagnostics); + } +} diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParserTest.java b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParserTest.java new file mode 100644 index 000000000..f23dece83 --- /dev/null +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bep/parsers/error/StderrDiagnosticsParserTest.java @@ -0,0 +1,322 @@ +package org.jetbrains.bsp.bazel.server.bep.parsers.error; + +import static org.junit.Assert.assertEquals; + +import ch.epfl.scala.bsp4j.BuildTargetIdentifier; +import ch.epfl.scala.bsp4j.Diagnostic; +import ch.epfl.scala.bsp4j.DiagnosticSeverity; +import ch.epfl.scala.bsp4j.Position; +import ch.epfl.scala.bsp4j.Range; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class StderrDiagnosticsParserTest { + + private final String error; + private final Map> expectedDiagnostics; + + public StderrDiagnosticsParserTest( + String error, Map> expectedDiagnostics) { + this.error = error; + this.expectedDiagnostics = expectedDiagnostics; + } + + @Parameters(name = "{index}: FileDiagnostic.fromError({0}) should equals {1}") + public static List data() { + return List.of( + new Object[][] { + { + // given - error in BUILD file + "Loading:\n" + + "Loading: 0 packages loaded\n" + + "Analyzing: target //path/to/package:test (0 packages loaded, 0 targets" + + " configured)\n" + + "INFO: Analyzed target //path/to/package:test (0 packages loaded, 0 targets" + + " configured).\n" + + "INFO: Found 1 target...\n" + + "[0 / 3] [Prepa] BazelWorkspaceStatusAction stable-status.txt\n" + + "ERROR: /user/workspace/path/to/package/BUILD:12:37: in java_test rule" + + " //path/to/package:test: target '//path/to/another/package:lib' is not visible" + + " from target '//path/to/package:test'. Check the visibility declaration of the" + + " former target if you think the dependency is legitimate", + // then + Map.of( + "/user/workspace/path/to/package/BUILD", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: in java_test rule" + + " //path/to/package:test: target '//path/to/another/package:lib'" + + " is not visible from target '//path/to/package:test'. Check the" + + " visibility declaration of the former target if you think the" + + " dependency is legitimate") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test")))) + }, + { + // given - error in one source file + "Loading:\n" + + "Loading: 0 packages loaded\n" + + "Analyzing: target //path/to/package:test (0 packages loaded, 0 targets" + + " configured)\n" + + "INFO: Analyzed target //path/to/package:test (0 packages loaded, 0 targets" + + " configured).\n" + + "INFO: Found 1 target...\n" + + "[0 / 3] [Prepa] BazelWorkspaceStatusAction stable-status.txt\n" + + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala //path/to/package:test" + + " failed: (Exit 1): scalac failed: error executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params\n" + + "path/to/package/Test.scala:3: error: type mismatch;\n" + + " found : String(\"test\")\n" + + " required: Int\n" + + " val foo: Int = \"test\"\n" + + " ^\n" + + "one error found\n" + + "Build failed\n" + + "java.lang.RuntimeException: Build failed\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:280)\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:63)\n" + + "\tat io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:92)\n" + + "\tat io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:46)\n" + + "\tat io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:26)\n" + + "Target //path/to/package:test failed to build\n" + + "Use --verbose_failures to see the command lines of failed build steps.\n" + + "INFO: Elapsed time: 0.220s, Critical Path: 0.09s\n" + + "INFO: 2 processes: 2 internal.\n" + + "FAILED: Build did NOT complete successfully", + // then + Map.of( + "/user/workspace/path/to/package/BUILD", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala" + + " //path/to/package:test failed: (Exit 1): scalac failed: error" + + " executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test"))), + "path/to/package/Test.scala", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(3, 0), new Position(3, 0)), + "path/to/package/Test.scala:3: error: type mismatch;\n" + + " found : String(\"test\")\n" + + " required: Int\n" + + " val foo: Int = \"test\"\n" + + " ^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test.scala", + new BuildTargetIdentifier("//path/to/package:test")))) + }, + { + // given - errors in two source files + "Loading:\n" + + "Loading: 0 packages loaded\n" + + "Analyzing: target //path/to/package:test (0 packages loaded, 0 targets" + + " configured)\n" + + "INFO: Analyzed target //path/to/package:test (0 packages loaded, 0 targets" + + " configured).\n" + + "INFO: Found 1 target...\n" + + "[0 / 3] [Prepa] BazelWorkspaceStatusAction stable-status.txt\n" + + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala //path/to/package:test" + + " failed (Exit 1): scalac failed: error executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params\n" + + "path/to/package/Test1.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^\n" + + "path/to/package/Test2.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^\n" + + "2 errors\n" + + "Build failed\n" + + "java.lang.RuntimeException: Build failed\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:280)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:63)\n" + + "at io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:92)\n" + + "at io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:46)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:26)\n" + + "Target //path/to/package:test failed to build\n" + + "Use --verbose_failures to see the command lines of failed build steps.\n" + + "INFO: Elapsed time: 0.216s, Critical Path: 0.12s\n" + + "INFO: 2 processes: 2 internal.\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully", + // then + Map.of( + "/user/workspace/path/to/package/BUILD", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala" + + " //path/to/package:test failed (Exit 1): scalac failed: error" + + " executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test"))), + "path/to/package/Test1.scala", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(21, 0), new Position(21, 0)), + "path/to/package/Test1.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test1.scala", + new BuildTargetIdentifier("//path/to/package:test"))), + "path/to/package/Test2.scala", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(37, 0), new Position(37, 0)), + "path/to/package/Test2.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test2.scala", + new BuildTargetIdentifier("//path/to/package:test")))) + }, + { + // given - errors in one source file + "Loading:\n" + + "Loading: 0 packages loaded\n" + + "Analyzing: target //path/to/package:test (0 packages loaded, 0 targets" + + " configured)\n" + + "INFO: Analyzed target //path/to/package:test (0 packages loaded, 0 targets" + + " configured).\n" + + "INFO: Found 1 target...\n" + + "[0 / 3] [Prepa] BazelWorkspaceStatusAction stable-status.txt\n" + + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala //path/to/package:test" + + " failed (Exit 1): scalac failed: error executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params\n" + + "path/to/package/Test.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^\n" + + "path/to/package/Test.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^\n" + + "2 errors\n" + + "Build failed\n" + + "java.lang.RuntimeException: Build failed\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.compileScalaSources(ScalacWorker.java:280)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.work(ScalacWorker.java:63)\n" + + "at io.bazel.rulesscala.worker.Worker.persistentWorkerMain(Worker.java:92)\n" + + "at io.bazel.rulesscala.worker.Worker.workerMain(Worker.java:46)\n" + + "at io.bazel.rulesscala.scalac.ScalacWorker.main(ScalacWorker.java:26)\n" + + "Target //path/to/package:test failed to build\n" + + "Use --verbose_failures to see the command lines of failed build steps.\n" + + "INFO: Elapsed time: 0.216s, Critical Path: 0.12s\n" + + "INFO: 2 processes: 2 internal.\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully\n" + + "FAILED: Build did NOT complete successfully", + // then + Map.of( + "/user/workspace/path/to/package/BUILD", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(12, 37), new Position(12, 37)), + "ERROR: /user/workspace/path/to/package/BUILD:12:37: scala" + + " //path/to/package:test failed (Exit 1): scalac failed: error" + + " executing command" + + " bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/io_bazel_rules_scala/src/java/io/bazel/rulesscala/scalac/scalac" + + " @bazel-out/darwin-fastbuild/bin/path/to/package/test.jar-0.params") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "/user/workspace/path/to/package/BUILD", + new BuildTargetIdentifier("//path/to/package:test"))), + "path/to/package/Test.scala", + List.of( + new FileDiagnostic( + new Diagnostic( + new Range(new Position(21, 0), new Position(21, 0)), + "path/to/package/Test.scala:21: error: type mismatch;\n" + + "found : Int(42)\n" + + "required: String\n" + + "val x: String = 42\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test.scala", + new BuildTargetIdentifier("//path/to/package:test")), + new FileDiagnostic( + new Diagnostic( + new Range(new Position(37, 0), new Position(37, 0)), + "path/to/package/Test.scala:37: error: type mismatch;\n" + + "found : String(\"test\")\n" + + "required: Int\n" + + "val x: Int = \"test\"\n" + + "^") { + { + setSeverity(DiagnosticSeverity.ERROR); + } + }, + "path/to/package/Test.scala", + new BuildTargetIdentifier("//path/to/package:test")))) + } + }); + } + + @Test + public void shouldParseEntireEventStderr() { + // when + var diagnostics = StderrDiagnosticsParser.parse(error); + + // then + assertEquals(expectedDiagnostics, diagnostics); + } +} diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/BUILD b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/BUILD index e2cb2ee14..e22f945c5 100644 --- a/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/BUILD +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/BUILD @@ -14,3 +14,29 @@ java_test( "@maven//:org_assertj_assertj_core", ], ) + +java_test( + name = "SourceRootGuesserTest", + size = "small", + srcs = ["SourceRootGuesserTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//commons", + "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils", + ], +) + +java_test( + name = "InternalAspectsResolverTest", + size = "small", + srcs = ["InternalAspectsResolverTest.java"], + runtime_deps = [ + "@maven//:junit_junit", + ], + deps = [ + "//commons", + "//server/src/main/java/org/jetbrains/bsp/bazel/server/bsp/utils", + ], +) diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolverTest.java b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolverTest.java new file mode 100644 index 000000000..ba81585e9 --- /dev/null +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/InternalAspectsResolverTest.java @@ -0,0 +1,55 @@ +package org.jetbrains.bsp.bazel.server.bsp.utils; + +import static org.junit.Assert.assertEquals; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class InternalAspectsResolverTest { + + private Path workspaceRoot; + private Path bspRoot; + private String aspectName; + private String expectedAspectLabel; + + public InternalAspectsResolverTest( + String workspaceRoot, String bspRoot, String aspectName, String expectedAspectLabel) { + this.workspaceRoot = Paths.get(workspaceRoot); + this.bspRoot = Paths.get(bspRoot); + this.aspectName = aspectName; + this.expectedAspectLabel = expectedAspectLabel; + } + + @Test + public void shouldResolveLabels() { + String actual = new InternalAspectsResolver(bspRoot, workspaceRoot).resolveLabel(aspectName); + assertEquals(expectedAspectLabel, actual); + } + + @Parameters + public static Collection data() { + return Arrays.asList( + (Object[]) + new Object[][] { + { + "/Users/user/workspace/test-project", + "/Users/user/workspace/test-project", + "get_classpath", + "@//.bazelbsp:aspects.bzl%get_classpath" + }, + { + "/Users/user/workspace/test-project", + "/Users/user/workspace/test-project/bsp-projects/test-project-bsp", + "get_classpath", + "@//bsp-projects/test-project-bsp/.bazelbsp:aspects.bzl%get_classpath" + }, + }); + } +} diff --git a/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesserTest.java b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesserTest.java new file mode 100644 index 000000000..a8865d582 --- /dev/null +++ b/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesserTest.java @@ -0,0 +1,51 @@ +package org.jetbrains.bsp.bazel.server.bsp.utils; + +import static org.junit.Assert.assertEquals; + +import java.net.URI; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(value = Parameterized.class) +public class SourceRootGuesserTest { + + private URI input; + private String expectedOutput; + + public SourceRootGuesserTest(String input, String expected) throws Exception { + this.input = (new URL(input)).toURI(); + this.expectedOutput = expected; + } + + @Test + public void shouldGuessSourceRoots() { + String output = SourceRootGuesser.getSourcesRoot(input); + assertEquals(expectedOutput, output); + } + + @Parameters + public static Collection data() { + return Arrays.asList( + (Object[]) + new Object[][] { + { + "file:///WORKSPACE_ROOT/java_hello/src/main/java/com/hello/Hello.java", + "/WORKSPACE_ROOT/java_hello/src/main/java" + }, + { + "file:///WORKSPACE_ROOT/server/src/test/java/org/jetbrains/bsp/bazel/server/bsp/utils/SourceRootGuesserTest.java", + "/WORKSPACE_ROOT/server/src/test/java" + }, + { + "file:///WORKSPACE_ROOT/src/main/java/org/test/java", + "/WORKSPACE_ROOT/src/main/java/org/test/java" + }, + {"file:///WORKSPACE_ROOT/foo/bar", "/WORKSPACE_ROOT/foo"} + }); + } +} diff --git a/third_party.bzl b/third_party.bzl index dfef8879d..233612627 100644 --- a/third_party.bzl +++ b/third_party.bzl @@ -27,6 +27,7 @@ _deps = [ _dependency("org.apache.logging.log4j:log4j-core:2.6.1"), _dependency("junit:junit:4.12"), _dependency("org.assertj:assertj-core:3.16.1"), + _dependency("org.apache.commons:commons-collections4:jar:4.4"), ] def dependencies():