Repository: conan-io/conan

Issue:
[bug] Bazel server needs to be killed
### Describe the bug

I tried to create a Conan package of a library built with Bazel. The dependencies of the library, however, are modeled in Bazel. Everything worked fine as long as the parameter "clean" to the Bazel.build function was set to False, however upon using True I faced the following issue when running `conan create .`:

```
WARNING: Running Bazel server needs to be killed, because the startup options are different.
WARNING: Waiting for server process to terminate (waited 5 seconds, waiting at most 60)
```

As it turns out, the issue seems to be that bazel is indeed called with different startup options:

<https://github.com/conan-io/conan/blob/2.8.0/conan/tools/google/bazel.py#L71>
```python
    if clean:
        self._safe_run_command("bazel clean")
    self._safe_run_command(command)
```

results in 2 bazel calls:

```
bazel clean
bazel --bazelrc=/home/hvolx/.conan2/p/b/mylidcf3c80061eae/b/conan/conan_bzl.rc --bazelrc=/workspace/dev/.bazelrc build --config=conan-config //my_folder/mylib:mylib
```

As my .bazelrc file

```
# bazel from apt needs access to this cacerts location
startup --host_jvm_args=-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts
```

changes, the bazel startup options, the startup options are indeed different for above bazel commands.

I propose the following to fix the issue:

```python
def build(self, args=None, target="//...", clean=True):
        """
        Runs "bazel <rcpaths> build <configs> <args> <targets>" command where:

        * ``rcpaths``: adds ``--bazelrc=xxxx`` per rc-file path. It listens to ``BazelToolchain``
          (``--bazelrc=conan_bzl.rc``), and ``tools.google.bazel:bazelrc_path`` conf.
        * ``configs``: adds ``--config=xxxx`` per bazel-build configuration.
          It listens to ``BazelToolchain`` (``--config=conan-config``), and
          ``tools.google.bazel:configs`` conf.
        * ``args``: they are any extra arguments to add to the ``bazel build`` execution.
        * ``targets``: all the target labels.

        :param target: It is the target label. By default, it's "//..." which runs all the targets.
        :param args: list of extra arguments to pass to the CLI.
        :param clean: boolean that indicates to run a "bazel clean" before running the "bazel build".
                      Notice that this is important to ensure a fresh bazel cache every
        """
        # Use BazelToolchain generated file if exists
        conan_bazelrc = os.path.join(self._conanfile.generators_folder, BazelToolchain.bazelrc_name)
        use_conan_config = os.path.exists(conan_bazelrc)
        bazelrc_paths = []
        bazelrc_configs = []
        if use_conan_config:
            bazelrc_paths.append(conan_bazelrc)
            bazelrc_configs.append(BazelToolchain.bazelrc_config)
        # User bazelrc paths have more prio than Conan one
        # See more info in https://bazel.build/run/bazelrc
        bazelrc_paths.extend(self._conanfile.conf.get("tools.google.bazel:bazelrc_path", default=[],
                                                      check_type=list))
        # Note: In case of error like this: ... https://bcr.bazel.build/: PKIX path building failed
        # Check this comment: https://github.com/bazelbuild/bazel/issues/3915#issuecomment-1120894057
        bazel_exe = "bazel"
        startup_options = ""
        for rc in bazelrc_paths:
            rc = rc.replace("\\", "/")
            startup_options += f" --bazelrc={rc}"
        build_command = bazel_exe + startup_options + " build"
        bazelrc_configs.extend(self._conanfile.conf.get("tools.google.bazel:configs", default=[],
                                                        check_type=list))
        for config in bazelrc_configs:
            build_command += f" --config={config}"
        if args:
            build_command += " ".join(f" {arg}" for arg in args)
        build_command += f" {target}"
        if clean:
            clean_command = bazel_exe + startup_options + " clean"
            self._safe_run_command(clean_command)
        self._safe_run_command(build_command)
```

### More information:


Ubuntu 20.04
Conan version 2.8.0
Bazel 7.2.1

Conan config:

```shell
tools.google.bazel:bazelrc_path = ["/workspace/dev/.bazelrc"]
```

conanfily.py

```python
from conan import ConanFile
from conan.tools.google import Bazel, BazelToolchain
from conan.tools.google.layout import bazel_layout
from conan.tools.build import can_run
from conan.tools.files import copy
import os
required_conan_version = ">=2.0"

class mylibRecipe(ConanFile):
    name = "mylib"
    version = "0.0.1"
    package_type = "library"

    # Optional metadata
    license = "proprietary"
    author = "hvolx"
    url = "<Package recipe repository url here, for issues about the package>"
    description = "<Description of mylib package here>"
    topics = ("<Put some tag here>", "<here>", "<and here>")

    # Binary configuration
    settings = "os", "compiler", "build_type", "arch"
    options = {
        "shared": [True, False],
        "fPIC": [True, False],
    }
    default_options = {"shared": True, "fPIC": True}

    # Sources are located in the same place as this recipe, copy them to the recipe
    exports_sources = \
        "my_folder/mylib/BUILD.bazel", "my_folder/mylib/src/*", "my_folder/mylib/include/*", "my_folder/mylib/test/*", "MODULE.bazel", "tools/*"

    def layout(self):
        bazel_layout(self)

    def generate(self):
        tc = BazelToolchain(self)
        tc.cppstd="-std=c++17"
        tc.bazelrc_name=".bazelrc"
        tc.generate()

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def build(self):
        bazel = Bazel(self)
        bazel.build(target="//my_folder/mylib:mylib")
        if can_run(self):
            bazel.test()

    def package(self):
        bazel = Bazel(self)
        bazel._safe_run_command("bazel run //my_folder/mylib:install --define=deploy_folder="+os.path.join(self.package_folder,"lib"))

    def package_info(self):
        self.cpp_info.libs = ["mylib"]

    def build_requirements(self):
        self.tool_requires("bazel/7.2.1")

    def deploy(self):
        copy(self, "*", src=self.package_folder, dst=self.deploy_folder)
```


### How to reproduce it

1. Write .bazelrc file which changes bazel startup options
2. Use the .bazelrc in Conan config
3. Use the Conan Bazel build with clean=True
4. Run `conan create .`


Discussion:
Hi @hvolx 

Thanks a lot for reporting this. Yes, you're completely right 😁 
Proposing the fix: https://github.com/conan-io/conan/pull/17183



Patch:
diff --git a/conan/tools/google/bazel.py b/conan/tools/google/bazel.py
index e0a8cbb4d9d..55702d2db26 100644
--- a/conan/tools/google/bazel.py
+++ b/conan/tools/google/bazel.py
@@ -11,6 +11,10 @@ def __init__(self, conanfile):
         :param conanfile: ``< ConanFile object >`` The current recipe object. Always use ``self``.
         """
         self._conanfile = conanfile
+        # Use BazelToolchain generated file if exists
+        self._conan_bazelrc = os.path.join(self._conanfile.generators_folder, BazelToolchain.bazelrc_name)
+        self._use_conan_config = os.path.exists(self._conan_bazelrc)
+        self._startup_opts = self._get_startup_command_options()
 
     def _safe_run_command(self, command):
         """
@@ -22,7 +26,18 @@ def _safe_run_command(self, command):
             self._conanfile.run(command)
         finally:
             if platform.system() == "Windows":
-                self._conanfile.run("bazel shutdown")
+                self._conanfile.run("bazel" + self._startup_opts + " shutdown")
+
+    def _get_startup_command_options(self):
+        bazelrc_paths = []
+        if self._use_conan_config:
+            bazelrc_paths.append(self._conan_bazelrc)
+        # User bazelrc paths have more prio than Conan one
+        # See more info in https://bazel.build/run/bazelrc
+        bazelrc_paths.extend(self._conanfile.conf.get("tools.google.bazel:bazelrc_path", default=[],
+                                                      check_type=list))
+        opts = " ".join(["--bazelrc=" + rc.replace("\\", "/") for rc in bazelrc_paths])
+        return f" {opts}" if opts else ""
 
     def build(self, args=None, target="//...", clean=True):
         """
@@ -41,34 +56,21 @@ def build(self, args=None, target="//...", clean=True):
         :param clean: boolean that indicates to run a "bazel clean" before running the "bazel build".
                       Notice that this is important to ensure a fresh bazel cache every
         """
-        # Use BazelToolchain generated file if exists
-        conan_bazelrc = os.path.join(self._conanfile.generators_folder, BazelToolchain.bazelrc_name)
-        use_conan_config = os.path.exists(conan_bazelrc)
-        bazelrc_paths = []
-        bazelrc_configs = []
-        if use_conan_config:
-            bazelrc_paths.append(conan_bazelrc)
-            bazelrc_configs.append(BazelToolchain.bazelrc_config)
-        # User bazelrc paths have more prio than Conan one
-        # See more info in https://bazel.build/run/bazelrc
-        bazelrc_paths.extend(self._conanfile.conf.get("tools.google.bazel:bazelrc_path", default=[],
-                                                      check_type=list))
         # Note: In case of error like this: ... https://bcr.bazel.build/: PKIX path building failed
         # Check this comment: https://github.com/bazelbuild/bazel/issues/3915#issuecomment-1120894057
-        command = "bazel"
-        for rc in bazelrc_paths:
-            rc = rc.replace("\\", "/")
-            command += f" --bazelrc={rc}"
-        command += " build"
-        bazelrc_configs.extend(self._conanfile.conf.get("tools.google.bazel:configs", default=[],
+        bazelrc_build_configs = []
+        if self._use_conan_config:
+            bazelrc_build_configs.append(BazelToolchain.bazelrc_config)
+        command = "bazel" + self._startup_opts + " build"
+        bazelrc_build_configs.extend(self._conanfile.conf.get("tools.google.bazel:configs", default=[],
                                                         check_type=list))
-        for config in bazelrc_configs:
+        for config in bazelrc_build_configs:
             command += f" --config={config}"
         if args:
             command += " ".join(f" {arg}" for arg in args)
         command += f" {target}"
         if clean:
-            self._safe_run_command("bazel clean")
+            self._safe_run_command("bazel" + self._startup_opts + " clean")
         self._safe_run_command(command)
 
     def test(self, target=None):
@@ -77,4 +79,4 @@ def test(self, target=None):
         """
         if self._conanfile.conf.get("tools.build:skip_test", check_type=bool) or target is None:
             return
-        self._safe_run_command(f'bazel test {target}')
+        self._safe_run_command("bazel" + self._startup_opts + f" test {target}")


Test patch:
diff --git a/test/unittests/tools/google/test_bazel.py b/test/unittests/tools/google/test_bazel.py
index 5c5aa583693..3aa21d364d5 100644
--- a/test/unittests/tools/google/test_bazel.py
+++ b/test/unittests/tools/google/test_bazel.py
@@ -54,8 +54,10 @@ def test_bazel_command_with_config_values():
     conanfile.conf.define("tools.google.bazel:bazelrc_path", ["/path/to/bazelrc"])
     bazel = Bazel(conanfile)
     bazel.build(target='//test:label')
+    commands = conanfile.commands
     assert "bazel --bazelrc=/path/to/bazelrc build " \
-           "--config=config --config=config2 //test:label" in conanfile.commands
+           "--config=config --config=config2 //test:label" in commands
+    assert "bazel --bazelrc=/path/to/bazelrc clean" in commands
 
 
 @pytest.mark.parametrize("path, pattern, expected", [