Java और Bazel

समस्या की शिकायत करें सोर्स देखें

इस पेज में ऐसे संसाधन मौजूद हैं जो Java प्रोजेक्ट के साथ Bazel का इस्तेमाल करने में आपकी मदद करते हैं. यह Bazel के साथ JavaScript प्रोजेक्ट बनाने के लिए खास ट्यूटोरियल, बिल्ड के नियम, और अन्य जानकारी से लिंक होता है.

Bazel के साथ काम करना

नीचे दिए गए संसाधन, Java प्रोजेक्ट पर Bazel के साथ काम करने में आपकी मदद करेंगे:

Bazel पर माइग्रेट करना

अगर आप फ़िलहाल Maven की मदद से अपने Java प्रोजेक्ट बना रहे हैं, तो Bazel की मदद से अपने Maven प्रोजेक्ट बनाना शुरू करने के लिए, माइग्रेशन गाइड में दिए गए तरीके अपनाएं:

Java के वर्शन

Java के ऐसे दो काम के वर्शन हैं जिन्हें कॉन्फ़िगरेशन फ़्लैग के साथ सेट किया गया है:

  • रिपॉज़िटरी में सोर्स फ़ाइलों का वर्शन
  • Java रनटाइम का वर्शन, जिसका इस्तेमाल कोड को एक्ज़ीक्यूट करने और उसे टेस्ट करने के लिए किया जाता है

डेटा स्टोर करने की आपकी जगह में मौजूद सोर्स कोड के वर्शन को कॉन्फ़िगर करना

अतिरिक्त कॉन्फ़िगरेशन के बिना, Bazel यह मान लेता है कि रिपॉज़िटरी में मौजूद सभी Java सोर्स फ़ाइलें, एक ही Java वर्शन में लिखी गई हैं. रिपॉज़िटरी में सोर्स का वर्शन बताने के लिए, .bazelrc फ़ाइल में build --java_language_version={ver} जोड़ें. यहां {ver}, उदाहरण के लिए 11 है. Bazel का डेटा स्टोर करने की जगह के मालिकों को यह फ़्लैग सेट करना चाहिए, ताकि Bazel और उसके उपयोगकर्ता, सोर्स कोड के Java वर्शन नंबर का रेफ़रंस दे सकें. ज़्यादा जानकारी के लिए, Java भाषा के वर्शन का फ़्लैग देखें.

कोड को चलाने और उसकी जांच करने के लिए इस्तेमाल किए जाने वाले JVM को कॉन्फ़िगर करना

Bazel, कंपाइल करने के लिए एक JDK का इस्तेमाल करता है और कोड को एक्ज़ीक्यूट करने और उसकी जांच करने के लिए, दूसरा JVM इस्तेमाल करता है.

डिफ़ॉल्ट रूप से Bazel, डाउनलोड होने वाले JDK का इस्तेमाल करके कोड को कंपाइल करता है. साथ ही, लोकल मशीन पर इंस्टॉल किए गए JVM के साथ कोड को एक्ज़ीक्यूट करता है और उसकी जांच करता है. Bazel, JAVA_HOME या पाथ का इस्तेमाल करके JVM को खोजता है.

इससे बनने वाली बाइनरी, सिस्टम लाइब्रेरी में स्थानीय तौर पर इंस्टॉल किए गए जेवीएम के साथ काम करती हैं. इसका मतलब है कि नतीजे में मिलने वाली बाइनरी, मशीन पर इंस्टॉल की गई चीज़ पर निर्भर करती हैं.

एक्ज़ीक्यूशन और टेस्टिंग के लिए इस्तेमाल किए जाने वाले जेवीएम को कॉन्फ़िगर करने के लिए, --java_runtime_version फ़्लैग का इस्तेमाल करें. डिफ़ॉल्ट वैल्यू local_jdk है.

हर्मेटिक टेस्टिंग और कंपाइलेशन

हर्मेटिक कंपाइल बनाने के लिए, कमांड लाइन फ़्लैग का इस्तेमाल किया जा सकता है --java_runtime_version=remotejdk_11. रिमोट रिपॉज़िटरी से डाउनलोड की गई JVM पर कोड को कंपाइल किया जाता है, उसे एक्ज़ीक्यूट किया जाता है, और उसकी जांच की जाती है. ज़्यादा जानकारी के लिए, Java रनटाइम वर्शन फ़्लैग देखें.

Java में बिल्ड टूल को कंपाइल करना और उसे एक्ज़ीक्यूट करना

टूल बनाने और उन्हें एक्ज़ीक्यूट करने के लिए, JDK और JVM का दूसरा पेयर है. इनका इस्तेमाल बिल्ड प्रोसेस में तो किया जाता है, लेकिन बिल्ड के नतीजों में नहीं. उस जेडीके और जेवीएम को --tool_java_language_version और --tool_java_runtime_version की मदद से कंट्रोल किया जाता है. डिफ़ॉल्ट रूप से, ये वैल्यू 11 और remotejdk_11 होती हैं.

लोकल स्टोरेज में इंस्टॉल किए गए JDK का इस्तेमाल करके कंपाइल किया जा रहा है

डिफ़ॉल्ट रूप से Bazel, रिमोट JDK का इस्तेमाल करके कंपाइल करता है, क्योंकि यह JDK के इंटरनल को बदल देता है. लोकल स्टोरेज में इंस्टॉल किए गए JDK का इस्तेमाल करने वाले कंपाइल टूलचेन को कॉन्फ़िगर किया जाता है. हालांकि, इसका इस्तेमाल नहीं किया जाता.

लोकल जेडीके का इस्तेमाल करके कंपाइल करने के लिए, लोकल जेडीके के लिए कंपाइलेशन टूलचेन का इस्तेमाल करें. अतिरिक्त फ़्लैग --extra_toolchains=@local_jdk//:all का इस्तेमाल करें. हालांकि, ध्यान रखें कि हो सकता है कि यह आर्बिट्रेरी वेंडर के जेडीके पर काम न करे.

ज़्यादा जानकारी के लिए, Java टूलचेन कॉन्फ़िगर करना देखें.

सबसे सही तरीके

Bazel के सबसे सही तरीकों के अलावा, नीचे Java प्रोजेक्ट के लिए सबसे सही तरीके बताए गए हैं.

डायरेक्ट्री का स्ट्रक्चर

Maven के स्टैंडर्ड डायरेक्ट्री लेआउट को प्राथमिकता दें (src/main/java वाले सोर्स और src/test/java से कम की जांच वाले सोर्स).

फ़ाइलें बनाएं

अपनी BUILD फ़ाइलें बनाते समय इन दिशा-निर्देशों का पालन करें:

  • Java सोर्स वाली हर डायरेक्ट्री के लिए एक BUILD फ़ाइल इस्तेमाल करें, क्योंकि इससे बिल्ड की परफ़ॉर्मेंस बेहतर होती है.

  • हर BUILD फ़ाइल में एक java_library नियम होना चाहिए, जो ऐसा दिखता है:

    java_library(
        name = "directory-name",
        srcs = glob(["*.java"]),
        deps = [...],
    )
    
  • लाइब्रेरी का नाम उस डायरेक्ट्री का नाम होना चाहिए जिसमें BUILD फ़ाइल है. इससे लाइब्रेरी का लेबल छोटा हो जाता है, जो "//package:package" के बजाय "//package" का इस्तेमाल करता है.

  • सोर्स, डायरेक्ट्री में मौजूद सभी Java फ़ाइलों में से, ऐसे glob होने चाहिए जिन्हें बार-बार नहीं किया जाता.

  • टेस्ट, src/test में मौजूद मिलती-जुलती डायरेक्ट्री में होने चाहिए. साथ ही, यह इस लाइब्रेरी पर निर्भर करता है.

बेहतर Java बिल्ड के लिए नए नियम बनाना

ध्यान दें: नए नियम, बेहतर बिल्ड और टेस्ट की स्थितियों के लिए बनाए जाते हैं. आपको Bazel का इस्तेमाल शुरू करते समय इसकी ज़रूरत नहीं है.

नीचे दिए गए मॉड्यूल, कॉन्फ़िगरेशन फ़्रैगमेंट, और सेवा देने वाली कंपनियां, आपके Java प्रोजेक्ट बनाते समय Bazel की क्षमताओं को बढ़ाने में आपकी मदद करेंगी:

Java टूलचेन को कॉन्फ़िगर करना

Bazel दो तरह के Java टूलचेन का इस्तेमाल करता है: - एक्ज़ीक्यूशन, Java बाइनरी को एक्ज़ीक्यूट करने और उसकी जांच करने के लिए, --java_runtime_version फ़्लैग से कंट्रोल करता है - कंपाइलेशन, जावा सोर्स को कंपाइल करने में इस्तेमाल किया जाता है, --java_language_version फ़्लैग से कंट्रोल किया जाता है

प्रोग्राम चलाने के लिए अतिरिक्त टूलचेन कॉन्फ़िगर करना

एक्ज़ीक्यूशन टूलचेन, जेवीएम है, जो लोकल या रिपॉज़िटरी (डेटा स्टोर करने की जगह) से लिया जाता है. इसमें, इसके वर्शन, ऑपरेटिंग सिस्टम, और सीपीयू आर्किटेक्चर के बारे में कुछ अतिरिक्त जानकारी होती है.

WORKSPACE फ़ाइल में, local_java_repository या remote_java_repository नियमों का इस्तेमाल करके, Java की प्रोसेस करने वाले टूलचेन जोड़े जा सकते हैं. नियम जोड़ने पर, फ़्लैग का इस्तेमाल करके जेवीएम उपलब्ध हो जाता है. जब एक ही ऑपरेटिंग सिस्टम और सीपीयू आर्किटेक्चर के लिए कई परिभाषाएं दी जाती हैं, तो पहली परिभाषा का इस्तेमाल किया जाता है.

लोकल जेवीएम के कॉन्फ़िगरेशन का उदाहरण:

load("@bazel_tools//tools/jdk:local_java_repository.bzl", "local_java_repository")

local_java_repository(
  name = "additionaljdk",          # Can be used with --java_runtime_version=additionaljdk, --java_runtime_version=11 or --java_runtime_version=additionaljdk_11
  version = 11,                    # Optional, if not set it is autodetected
  java_home = "/usr/lib/jdk-15/",  # Path to directory containing bin/java
)

रिमोट जेवीएम के कॉन्फ़िगरेशन का उदाहरण:

load("@bazel_tools//tools/jdk:remote_java_repository.bzl", "remote_java_repository")

remote_java_repository(
  name = "openjdk_canary_linux_arm",
  prefix = "openjdk_canary", # Can be used with --java_runtime_version=openjdk_canary_11
  version = "11",            # or --java_runtime_version=11
  target_compatible_with = [ # Specifies constraints this JVM is compatible with
    "@platforms//cpu:arm",
    "@platforms//os:linux",
  ],
  urls = ...,               # Other parameters are from http_repository rule.
  sha256 = ...,
  strip_prefix = ...
)

अतिरिक्त कंपाइलेशन टूलचेन को कॉन्फ़िगर करना

कंपाइलेशन टूलचेन में, JDK और कई ऐसे टूल शामिल होते हैं जिनका इस्तेमाल Bazel, कंपाइलेशन के दौरान करता है. इसमें कुछ अतिरिक्त सुविधाएं भी शामिल होती हैं. जैसे: गड़बड़ी प्रोन, सख्त Java डिपेंडेंसी, हेडर को कंपाइल करना, Android को डीयूज़ करना, कवरेज इंस्ट्रुमेंटेशन, और IDEs के लिए जेनक्लास हैंडलिंग.

JavaBuilder, Bazel के साथ बंडल किया गया एक टूल है. यह कंपाइलेशन का इस्तेमाल करता है और ऊपर बताई गई सुविधाएं उपलब्ध कराता है. असल कंपाइलर, JDK के इंटरनल कंपाइलर का इस्तेमाल करके एक्ज़ीक्यूट करता है. कंपाइलेशन के लिए इस्तेमाल किया जाने वाला JDK, टूलचेन के java_runtime एट्रिब्यूट से तय किया जाता है.

Bazel कुछ JDK इंटर्नल को बदल देता है. JDK वर्शन > 9 के मामले में, java.compiler और jdk.compiler मॉड्यूल को JDK के फ़्लैग --patch_module का इस्तेमाल करके पैच किया जाता है. JDK वर्शन 8 के मामले में, Java कंपाइलर को -Xbootclasspath फ़्लैग का इस्तेमाल करके पैच किया जाता है.

VanillaJavaBuilder, JavaBuilder को दूसरी जगह लागू करने का एक तरीका है. इसमें JDK के इंटरनल कंपाइलर में बदलाव नहीं किया जाता है. साथ ही, इसमें कोई भी अतिरिक्त सुविधा नहीं होती है. VanillaJavaBuilder से पहले से मौजूद किसी भी टूलचेन में इसका इस्तेमाल नहीं किया जाता है.

JavaBuilder के साथ-साथ, Bazel, कंपाइलेशन के दौरान कई दूसरे टूल का इस्तेमाल करता है.

ijar टूल, कॉल सिग्नेचर को छोड़कर बाकी सब कुछ हटाने के लिए jar फ़ाइलें प्रोसेस करता है. मिलने वाले जार को हेडर जार कहा जाता है. इनका इस्तेमाल कंपाइलेशन इंक्रीमेंटल को बेहतर बनाने के लिए किया जाता है. इसके लिए, डाउनस्ट्रीम डिपेंडेंट को फिर से कंपाइल करते हैं. ऐसा तब किया जाता है, जब फ़ंक्शन के मुख्य हिस्से में बदलाव होता है.

singlejar टूल की मदद से, कई jar फ़ाइलों को एक साथ पैक किया जा सकता है.

genclass टूल, Java कंपाइलेशन के आउटपुट को पोस्ट-प्रोसेस करता है और एक jar बनाता है. इसमें सिर्फ़ उन सोर्स की क्लास फ़ाइलें होती हैं जो एनोटेशन प्रोसेसर से जनरेट होती हैं.

JacocoRunner टूल, Jacoco को इंस्ट्रुमेंट वाली फ़ाइलों के ऊपर चलाता है और आउटपुट के तौर पर, LCOV फ़ॉर्मैट में नतीजे दिखाता है.

TestRunner टूल, कंट्रोल एनवायरमेंट में JUnit 4 टेस्ट करता है.

BUILD फ़ाइल में default_java_toolchain मैक्रो जोड़कर और उसे रजिस्टर करके, कंपाइलेशन को फिर से कॉन्फ़िगर किया जा सकता है. ऐसा WORKSPACE फ़ाइल में register_toolchains नियम जोड़कर या --extra_toolchains फ़्लैग का इस्तेमाल करके किया जा सकता है.

टूलचेन का इस्तेमाल सिर्फ़ तब किया जाता है, जब source_version एट्रिब्यूट, --java_language_version फ़्लैग की गई वैल्यू से मैच करता है.

टूलचेन कॉन्फ़िगरेशन के उदाहरण:

load(
  "@bazel_tools//tools/jdk:default_java_toolchain.bzl",
  "default_java_toolchain", "DEFAULT_TOOLCHAIN_CONFIGURATION", "BASE_JDK9_JVM_OPTS", "DEFAULT_JAVACOPTS"
)

default_java_toolchain(
  name = "repository_default_toolchain",
  configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,        # One of predefined configurations
                                                          # Other parameters are from java_toolchain rule:
  java_runtime = "@bazel_tools//tools/jdk:remote_jdk11", # JDK to use for compilation and toolchain's tools execution
  jvm_opts = BASE_JDK9_JVM_OPTS + ["--enable_preview"],   # Additional JDK options
  javacopts = DEFAULT_JAVACOPTS + ["--enable_preview"],   # Additional javac options
  source_version = "9",
)

इसे --extra_toolchains=//:repository_default_toolchain_definition का इस्तेमाल करके या वर्कस्पेस में register_toolchains("//:repository_default_toolchain_definition") जोड़कर इस्तेमाल किया जा सकता है.

पहले से तय कॉन्फ़िगरेशन:

  • DEFAULT_TOOLCHAIN_CONFIGURATION: सभी सुविधाएं, JDK वर्शन के साथ काम करती हैं >= 9
  • VANILLA_TOOLCHAIN_CONFIGURATION: कोई अतिरिक्त सुविधा नहीं है. यह आर्बिट्ररी वेंडर के जेडीके के साथ काम करता है.
  • PREBUILT_TOOLCHAIN_CONFIGURATION: डिफ़ॉल्ट की तरह है, लेकिन सिर्फ़ पहले से बने टूल (ijar, singlejar) का इस्तेमाल करें
  • NONPREBUILT_TOOLCHAIN_CONFIGURATION: यह डिफ़ॉल्ट रूप से काम करता है, लेकिन सभी टूल सोर्स से बने हैं (यह अलग-अलग Libc वाले ऑपरेटिंग सिस्टम पर काम आ सकता है)

JVM और Java कंपाइलर फ़्लैग कॉन्फ़िगर करना

JVM और javac फ़्लैग को फ़्लैग या default_java_toolchain एट्रिब्यूट के साथ कॉन्फ़िगर किया जा सकता है.

--jvmopt, --host_jvmopt, --javacopt, और --host_javacopt से जुड़े फ़्लैग हैं.

सही default_java_toolchain एट्रिब्यूट हैं: javacopts, jvm_opts, javabuilder_jvm_opts, और turbine_jvm_opts.

पैकेज से जुड़ा खास Java कंपाइलर फ़्लैग कॉन्फ़िगरेशन

आपके पास default_java_toolchain के package_configuration एट्रिब्यूट का इस्तेमाल करके, खास सोर्स फ़ाइलों के लिए अलग-अलग Java कंपाइलर फ़्लैग कॉन्फ़िगर करने का विकल्प होता है. कृपया नीचे दिया गया उदाहरण देखें.

load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain")

# This is a convenience macro that inherits values from Bazel's default java_toolchain
default_java_toolchain(
    name = "toolchain",
    package_configuration = [
        ":error_prone",
    ],
    visibility = ["//visibility:public"],
)

# This associates a set of javac flags with a set of packages
java_package_configuration(
    name = "error_prone",
    javacopts = [
        "-Xep:MissingOverride:ERROR",
    ],
    packages = ["error_prone_packages"],
)

# This is a regular package_group, which is used to specify a set of packages to apply flags to
package_group(
    name = "error_prone_packages",
    packages = [
        "//foo/...",
        "-//foo/bar/...", # this is an exclusion
    ],
)

एक ही जगह पर डेटा स्टोर करने की जगह में, Java सोर्स कोड के कई वर्शन

Bazel, बिल्ड में Java सोर्स के सिर्फ़ एक वर्शन को कंपाइल करने की सुविधा देता है. इसका मतलब है कि Java टेस्ट या कोई ऐप्लिकेशन बनाते समय, सभी डिपेंडेंसी, एक ही Java वर्शन के हिसाब से बनाई जाती हैं.

हालांकि, अलग-अलग फ़्लैग का इस्तेमाल करके अलग-अलग बिल्ड चलाए जा सकते हैं.

अलग-अलग फ़्लैग का इस्तेमाल आसान बनाने के लिए, किसी खास वर्शन के लिए फ़्लैग के सेट को .bazelrc कॉन्फ़िगरेशन के साथ ग्रुप किया जा सकता है:

build:java8 --java_language_version=8
build:java8 --java_runtime_version=local_jdk_8
build:java11 --java_language_version=11
build:java11 --java_runtime_version=remotejdk_11

इन कॉन्फ़िगरेशन का इस्तेमाल --config फ़्लैग के साथ किया जा सकता है, उदाहरण के लिए bazel test --config=java11 //:java11_test.